blob: 8e642de80f3c546ecf7c5618ac50e852e3a5a147 [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;
Doris Liu6c751642014-05-05 18:43:26 -070032import android.media.AudioManager;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.media.CameraProfile;
Doris Liu6c751642014-05-05 18:43:26 -070034import android.media.SoundPool;
Michael Kolb8872c232013-01-29 10:33:22 -080035import android.net.Uri;
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -070036import android.os.AsyncTask;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070037import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080038import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080039import android.os.Handler;
40import android.os.Looper;
41import android.os.Message;
42import android.os.MessageQueue;
43import android.os.SystemClock;
44import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080045import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080046import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080047import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080048
Sameer Padala2c8cc452013-11-05 18:49:12 -080049import com.android.camera.PhotoModule.NamedImages.NamedEntity;
50import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080051import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070052import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070053import com.android.camera.app.MediaSaver;
54import com.android.camera.app.MemoryManager;
55import com.android.camera.app.MemoryManager.MemoryListener;
Kevin Gabayanfb333362014-06-02 14:48:20 -070056import com.android.camera.app.MotionManager;
Angus Kong2bca2102014-03-11 16:27:30 -070057import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070058import com.android.camera.exif.ExifInterface;
59import com.android.camera.exif.ExifTag;
60import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080061import com.android.camera.hardware.HardwareSpec;
62import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080063import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080064import com.android.camera.remote.RemoteCameraModule;
Andy Huibers90c7ad52014-05-20 22:13:54 -070065import com.android.camera.settings.CameraPictureSizesCacher;
Erin Dahlgren6190c362014-06-13 14:12:08 -070066import com.android.camera.settings.Keys;
Angus Kong454d63f2014-05-06 14:45:59 -070067import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080068import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010069import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070070import com.android.camera.ui.CountDownView;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070071import com.android.camera.ui.TouchCoordinate;
Angus Kongdcccc512013-08-08 17:06:03 -070072import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070073import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070074import com.android.camera.util.GcamHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070075import com.android.camera.util.SessionStatsCollector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070076import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070077import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070078import com.android.camera2.R;
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
Erin Dahlgren6190c362014-06-13 14:12:08 -0700112 private static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
113
114 private static final Log.Tag TAG = new Log.Tag(PHOTO_MODULE_STRING_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800115
116 // We number the request code from 1000 to avoid collision with Gallery.
117 private static final int REQUEST_CROP = 1000;
118
Angus Kong13e87c42013-11-25 10:02:47 -0800119 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800120 private static final int MSG_FIRST_TIME_INIT = 1;
121 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800122
123 // The subset of parameters we need to update in setCameraParameters().
124 private static final int UPDATE_PARAM_INITIALIZE = 1;
125 private static final int UPDATE_PARAM_ZOOM = 2;
126 private static final int UPDATE_PARAM_PREFERENCE = 4;
127 private static final int UPDATE_PARAM_ALL = -1;
128
Andy Huibersdef975d2013-11-22 09:13:39 -0800129 // This is the delay before we execute onResume tasks when coming
130 // from the lock screen, to allow time for onPause to execute.
131 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800132
Ruben Brunkd7488272013-10-10 18:45:53 -0700133 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
134
Michael Kolb8872c232013-01-29 10:33:22 -0800135 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800136 private CameraProxy mCameraDevice;
137 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700138 private CameraCapabilities mCameraCapabilities;
Angus Kong6607dae2014-06-10 16:07:45 -0700139 private CameraSettings mCameraSettings;
Michael Kolb8872c232013-01-29 10:33:22 -0800140 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800141
142 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800143
Michael Kolb8872c232013-01-29 10:33:22 -0800144 // The activity is going to switch to the specified camera id. This is
145 // needed because texture copy is done in GL thread. -1 means camera is not
146 // switching.
147 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800148
149 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
150 // needed to be updated in mUpdateSet.
151 private int mUpdateSet;
152
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800153 private int mZoomValue; // The current zoom value.
Andy Huibers547d7c82014-05-20 23:03:18 -0700154 private int mTimerDuration;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700155 /** Set when a volume button is clicked to take photo */
156 private boolean mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800157
Michael Kolb8872c232013-01-29 10:33:22 -0800158 private boolean mFocusAreaSupported;
159 private boolean mMeteringAreaSupported;
160 private boolean mAeLockSupported;
161 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700162 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800163
164 // The degrees of the device rotated clockwise from its natural orientation.
165 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800166
167 private static final String sTempCropFilename = "crop-temp";
168
Michael Kolb8872c232013-01-29 10:33:22 -0800169 private boolean mFaceDetectionStarted = false;
170
Michael Kolb8872c232013-01-29 10:33:22 -0800171 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
172 private String mCropValue;
173 private Uri mSaveUri;
174
Ruben Brunkd217ed02013-10-08 23:31:13 -0700175 private Uri mDebugUri;
176
Angus Kongce5480e2013-01-29 17:43:48 -0800177 // We use a queue to generated names of the images to be used later
178 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800179 private NamedImages mNamedImages;
180
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800181 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800182 @Override
183 public void run() {
184 onShutterButtonClick();
185 }
186 };
187
Michael Kolb8872c232013-01-29 10:33:22 -0800188 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800189 * An unpublished intent flag requesting to return as soon as capturing is
190 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800191 */
192 private static final String EXTRA_QUICK_CAPTURE =
193 "android.intent.extra.quickCapture";
194
195 // The display rotation in degrees. This is only valid when mCameraState is
196 // not PREVIEW_STOPPED.
197 private int mDisplayRotation;
198 // The value for android.hardware.Camera.setDisplayOrientation.
199 private int mCameraDisplayOrientation;
200 // The value for UI components like indicators.
201 private int mDisplayOrientation;
Angus Kong831347d2014-06-16 16:07:28 -0700202 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees.
Michael Kolb8872c232013-01-29 10:33:22 -0800203 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700204 // Indicates whether we are using front camera
205 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800206 private boolean mFirstTimeInitialized;
207 private boolean mIsImageCaptureIntent;
208
Michael Kolb8872c232013-01-29 10:33:22 -0800209 private int mCameraState = PREVIEW_STOPPED;
210 private boolean mSnapshotOnIdle = false;
211
212 private ContentResolver mContentResolver;
213
Doris Liu2b906b82013-12-10 16:34:08 -0800214 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800215
Michael Kolb8872c232013-01-29 10:33:22 -0800216 private final PostViewPictureCallback mPostViewPictureCallback =
217 new PostViewPictureCallback();
218 private final RawPictureCallback mRawPictureCallback =
219 new RawPictureCallback();
220 private final AutoFocusCallback mAutoFocusCallback =
221 new AutoFocusCallback();
222 private final Object mAutoFocusMoveCallback =
223 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700224 ? new AutoFocusMoveCallback()
225 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800226
227 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
228
229 private long mFocusStartTime;
230 private long mShutterCallbackTime;
231 private long mPostViewPictureCallbackTime;
232 private long mRawPictureCallbackTime;
233 private long mJpegPictureCallbackTime;
234 private long mOnResumeTime;
235 private byte[] mJpegImageData;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700236 /** Touch coordinate for shutter button press. */
237 private TouchCoordinate mShutterTouchCoordinate;
238
Michael Kolb8872c232013-01-29 10:33:22 -0800239
240 // These latency time are for the CameraLatency test.
241 public long mAutoFocusTime;
242 public long mShutterLag;
243 public long mShutterToPictureDisplayedTime;
244 public long mPictureDisplayedToJpegCallbackTime;
245 public long mJpegCallbackFinishTime;
246 public long mCaptureStartTime;
247
248 // This handles everything about focus.
249 private FocusOverlayManager mFocusManager;
250
Doris Liubd1b8f92014-01-03 17:59:51 -0800251 private final int mGcamModeIndex;
Doris Liu6c751642014-05-05 18:43:26 -0700252 private final CountdownSoundPlayer mCountdownSoundPlayer = new CountdownSoundPlayer();
Doris Liubd1b8f92014-01-03 17:59:51 -0800253
Angus Kong6607dae2014-06-10 16:07:45 -0700254 private CameraCapabilities.SceneMode mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800255
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100256 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800257
Michael Kolb8872c232013-01-29 10:33:22 -0800258 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700259 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800260 private final float[] mGData = new float[3];
261 private final float[] mMData = new float[3];
262 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700263 private int mHeading = -1;
264
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800265 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700266 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700267
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800268 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800269 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800270 @Override
271 public void onMediaSaved(Uri uri) {
272 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700273 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800274 }
275 }
276 };
Angus Kong454d63f2014-05-06 14:45:59 -0700277 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800278
Angus Kong0b9eb5b2014-04-30 15:03:33 -0700279 private final Runnable mResumeTaskRunnable = new Runnable() {
280 @Override
281 public void run() {
282 onResumeTasks();
283 }
284 };
285
Doris Liudb8f9752014-05-12 15:25:13 -0700286 /**
Angus Kong3699c412014-05-23 15:31:09 -0700287 * We keep the flash setting before entering scene modes (HDR)
288 * and restore it after HDR is off.
289 */
290 private String mFlashModeBeforeSceneMode;
291
292 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700293 * This callback gets called when user select whether or not to
294 * turn on geo-tagging.
295 */
296 public interface LocationDialogCallback {
297 /**
298 * Gets called after user selected/unselected geo-tagging feature.
299 *
300 * @param selected whether or not geo-tagging feature is selected
301 */
302 public void onLocationTaggingSelected(boolean selected);
303 }
304
305 /**
306 * This callback defines the text that is shown in the aspect ratio selection
307 * dialog, provides the current aspect ratio, and gets notified when user changes
308 * aspect ratio selection in the dialog.
309 */
310 public interface AspectRatioDialogCallback {
311 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700312 * Returns current aspect ratio that is being used to set as default.
313 */
314 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
315
316 /**
317 * Gets notified when user has made the aspect ratio selection.
318 *
319 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700320 * @param dialogHandlingFinishedRunnable runnable to run when the operations
321 * needed to handle changes from dialog
322 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700323 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700324 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
325 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700326 }
327
Angus Kongdcccc512013-08-08 17:06:03 -0700328 private void checkDisplayRotation() {
329 // Set the display orientation if display rotation has changed.
330 // Sometimes this happens when the device is held upside
331 // down and camera app is opened. Rotation animation will
332 // take some time and the rotation value we have got may be
333 // wrong. Framework does not have a callback for this now.
334 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
335 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800336 }
Angus Kongdcccc512013-08-08 17:06:03 -0700337 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
338 mHandler.postDelayed(new Runnable() {
339 @Override
340 public void run() {
341 checkDisplayRotation();
342 }
343 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800344 }
345 }
346
347 /**
348 * This Handler is used to post message back onto the main thread of the
349 * application
350 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100351 private static class MainHandler extends Handler {
352 private final WeakReference<PhotoModule> mModule;
353
354 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800355 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100356 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800357 }
358
Michael Kolb8872c232013-01-29 10:33:22 -0800359 @Override
360 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100361 PhotoModule module = mModule.get();
362 if (module == null) {
363 return;
364 }
Michael Kolb8872c232013-01-29 10:33:22 -0800365 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800366 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100367 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800368 break;
369 }
370
Angus Kong13e87c42013-11-25 10:02:47 -0800371 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100372 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800373 break;
374 }
Michael Kolb8872c232013-01-29 10:33:22 -0800375 }
376 }
377 }
378
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700379 private void switchToGcamCapture() {
380 if (mActivity != null && mGcamModeIndex != 0) {
381 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700382 settingsManager.set(SettingsManager.SCOPE_GLOBAL,
383 Keys.KEY_CAMERA_HDR_PLUS, true);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700384
385 // Disable the HDR+ button to prevent callbacks from being
386 // queued before the correct callback is attached to the button
387 // in the new module. The new module will set the enabled/disabled
388 // of this button when the module's preferred camera becomes available.
389 ButtonManager buttonManager = mActivity.getButtonManager();
Erin Dahlgren15473d42014-05-14 14:01:31 -0700390 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700391
Seth Raphael274f6e92014-05-21 17:11:53 -0700392 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
393
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700394 // Do not post this to avoid this module switch getting interleaved with
395 // other button callbacks.
396 mActivity.onModeSelected(mGcamModeIndex);
397 }
398 }
399
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800400 /**
401 * Constructs a new photo module.
402 */
Angus Kongc4e66562013-11-22 23:03:21 -0800403 public PhotoModule(AppController app) {
404 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800405 mGcamModeIndex = app.getAndroidContext().getResources()
406 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800407 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700408
Michael Kolb8872c232013-01-29 10:33:22 -0800409 @Override
Spike Sprague159e6e92014-05-27 18:26:30 -0700410 public String getPeekAccessibilityString() {
411 return mAppController.getAndroidContext()
412 .getResources().getString(R.string.photo_accessibility_peek);
413 }
414
415 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -0700416 public String getModuleStringIdentifier() {
417 return PHOTO_MODULE_STRING_ID;
418 }
419
420 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100421 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
422 mActivity = activity;
423 // TODO: Need to look at the controller interface to see if we can get
424 // rid of passing in the activity directly.
425 mAppController = mActivity;
426
427 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
428 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800429
430 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700431 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(),
432 Keys.KEY_CAMERA_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800433
Doris Liudb8f9752014-05-12 15:25:13 -0700434 // TODO: Move this to SettingsManager as a part of upgrade procedure.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700435 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
436 Keys.KEY_USER_SELECTED_ASPECT_RATIO)) {
Doris Liudb8f9752014-05-12 15:25:13 -0700437 // Switch to back camera to set aspect ratio.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700438 mCameraId = settingsManager.getIntegerDefault(Keys.KEY_CAMERA_ID);
Doris Liudb8f9752014-05-12 15:25:13 -0700439 }
440
Michael Kolb8872c232013-01-29 10:33:22 -0800441 mContentResolver = mActivity.getContentResolver();
442
Michael Kolb8872c232013-01-29 10:33:22 -0800443 // Surface texture is from camera screen nail and startPreview needs it.
444 // This must be done before startPreview.
445 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800446
Michael Kolb8872c232013-01-29 10:33:22 -0800447 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800448 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700449 mUI.setCountdownFinishedListener(this);
450
451 // TODO: Make this a part of app controller API.
452 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
453 cancelButton.setOnClickListener(new View.OnClickListener() {
454 @Override
455 public void onClick(View view) {
456 cancelCountDown();
457 }
458 });
459 }
460
461 private void cancelCountDown() {
462 if (mUI.isCountingDown()) {
463 // Cancel on-going countdown.
464 mUI.cancelCountDown();
465 }
466 mAppController.getCameraAppUI().transitionToCapture();
467 mAppController.getCameraAppUI().showModeOptions();
Michael Kolbd6954f32013-03-08 20:43:01 -0800468 }
469
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800470 @Override
471 public boolean isUsingBottomBar() {
472 return true;
473 }
474
Michael Kolbd6954f32013-03-08 20:43:01 -0800475 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800476 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800477 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800478 setupCaptureParams();
479 }
480 }
481
482 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800483 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800484 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800485 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700486 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800487 }
488
Doris Liudb8f9752014-05-12 15:25:13 -0700489 /**
490 * Prompt the user to pick to record location and choose aspect ratio for the
491 * very first run of camera only.
492 */
493 private void settingsFirstRun() {
494 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100495
Doris Liudb8f9752014-05-12 15:25:13 -0700496 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800497 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800498 }
Doris Liudb8f9752014-05-12 15:25:13 -0700499
Erin Dahlgren6190c362014-06-13 14:12:08 -0700500 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SCOPE_GLOBAL,
501 Keys.KEY_RECORD_LOCATION);
Doris Liudb8f9752014-05-12 15:25:13 -0700502 boolean aspectRatioPrompt = !settingsManager.getBoolean(
Erin Dahlgren6190c362014-06-13 14:12:08 -0700503 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO);
Doris Liudb8f9752014-05-12 15:25:13 -0700504 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800505 return;
506 }
Doris Liudb8f9752014-05-12 15:25:13 -0700507
Michael Kolb8872c232013-01-29 10:33:22 -0800508 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800509 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800510 if (backCameraId == -1) {
511 // If there is no back camera, do not show the prompt.
512 return;
513 }
Doris Liudb8f9752014-05-12 15:25:13 -0700514
515 if (locationPrompt) {
516 // Show both location and aspect ratio selection dialog.
517 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700518 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700519 public void onLocationTaggingSelected(boolean selected) {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700520 Keys.setLocation(mActivity.getSettingsManager(), selected,
521 mActivity.getLocationManager());
Doris Liudb8f9752014-05-12 15:25:13 -0700522 }
523 }, createAspectRatioDialogCallback());
524 } else {
525 // App upgrade. Only show aspect ratio selection.
526 mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
527 }
528 }
529
530 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -0700531 Size currentSize = mCameraSettings.getCurrentPhotoSize();
Doris Liudb8f9752014-05-12 15:25:13 -0700532 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
533 if (aspectRatio < 1f) {
534 aspectRatio = 1 / aspectRatio;
535 }
536 final AspectRatioSelector.AspectRatio currentAspectRatio;
537 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
538 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
539 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
540 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
541 } else {
542 // TODO: Log error and not show dialog.
543 return null;
544 }
545
Angus Kong6607dae2014-06-10 16:07:45 -0700546 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes();
Doris Liudb8f9752014-05-12 15:25:13 -0700547 List<Size> pictureSizes = ResolutionUtil
548 .getDisplayableSizesFromSupported(sizes, true);
549
550 // This logic below finds the largest resolution for each aspect ratio.
551 // TODO: Move this somewhere that can be shared with SettingsActivity
552 int aspectRatio4x3Resolution = 0;
553 int aspectRatio16x9Resolution = 0;
554 Size largestSize4x3 = new Size(0, 0);
555 Size largestSize16x9 = new Size(0, 0);
556 for (Size size : pictureSizes) {
557 float pictureAspectRatio = (float) size.width() / (float) size.height();
558 pictureAspectRatio = pictureAspectRatio < 1 ?
559 1f / pictureAspectRatio : pictureAspectRatio;
560 int resolution = size.width() * size.height();
561 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
562 if (resolution > aspectRatio4x3Resolution) {
563 aspectRatio4x3Resolution = resolution;
564 largestSize4x3 = size;
565 }
566 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
567 if (resolution > aspectRatio16x9Resolution) {
568 aspectRatio16x9Resolution = resolution;
569 largestSize16x9 = size;
570 }
571 }
572 }
Doris Liudb8f9752014-05-12 15:25:13 -0700573
Doris Liu08b0cdd2014-05-13 19:19:55 -0700574 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
575 final Size size4x3ToSelect = largestSize4x3;
576 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700577
Doris Liudb8f9752014-05-12 15:25:13 -0700578 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700579
580 @Override
581 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
582 return currentAspectRatio;
583 }
584
585 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700586 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
587 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700588 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700589 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700590 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
591 Keys.KEY_PICTURE_SIZE_BACK,
592 largestSize4x3Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700593 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700594 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700595 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
596 Keys.KEY_PICTURE_SIZE_BACK,
597 largestSize16x9Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700598 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700599 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
600 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
601 String aspectRatio = mActivity.getSettingsManager().getString(
602 SettingsManager.SCOPE_GLOBAL,
603 Keys.KEY_USER_SELECTED_ASPECT_RATIO);
604 Log.e(TAG, "aspect ratio after setting it to true=" + aspectRatio);
Doris Liudb8f9752014-05-12 15:25:13 -0700605 if (newAspectRatio != currentAspectRatio) {
Doris Liudb8f9752014-05-12 15:25:13 -0700606 stopPreview();
607 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700608 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
609 } else {
610 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700611 }
612 }
613 };
614 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700615 }
Michael Kolb8872c232013-01-29 10:33:22 -0800616
ztenghui7b265a62013-09-09 14:58:44 -0700617 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700618 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800619 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700620 }
621
622 @Override
623 public void onPreviewUIDestroyed() {
624 if (mCameraDevice == null) {
625 return;
626 }
627 mCameraDevice.setPreviewTexture(null);
628 stopPreview();
629 }
630
Doris Liu1dfe7822013-12-12 00:02:08 -0800631 @Override
632 public void startPreCaptureAnimation() {
633 mAppController.startPreCaptureAnimation();
634 }
635
Michael Kolbd6954f32013-03-08 20:43:01 -0800636 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800637 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800638 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800639 }
640
Michael Kolbd6954f32013-03-08 20:43:01 -0800641 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800642 if (mPaused) {
643 return;
644 }
Doris Liu6c751642014-05-05 18:43:26 -0700645 cancelCountDown();
Doris Liu6809eb82014-05-21 13:16:59 -0700646
647 mAppController.freezeScreenUntilPreviewReady();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800648 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800649
Alan Newbergerd41766f2014-04-09 18:25:34 -0700650 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800651 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800652 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren6190c362014-06-13 14:12:08 -0700653 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800654 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800655 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800656 if (mFocusManager != null) {
657 mFocusManager.removeMessages();
658 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800659
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700660 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700661 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700662 // Start switch camera animation. Post a message because
663 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800664 }
665
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800666 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100667 new ButtonManager.ButtonCallback() {
668 @Override
669 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800670 // At the time this callback is fired, the camera id
671 // has be set to the desired camera.
672
Angus Kong97e282a2014-03-04 18:44:49 -0800673 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100674 return;
675 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800676 // If switching to back camera, and HDR+ is still on,
677 // switch back to gcam, otherwise handle callback normally.
678 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700679 if (Keys.isCameraBackFacing(settingsManager,
680 mAppController.getModuleScope())) {
681 if (Keys.requestsReturnToHdrPlus(settingsManager,
682 mAppController.getModuleScope())) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700683 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800684 return;
685 }
686 }
687
Sascha Haeberlingde303232014-02-07 02:30:53 +0100688 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800689
Alan Newbergerd41766f2014-04-09 18:25:34 -0700690 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100691 // We need to keep a preview frame for the animation before
692 // releasing the camera. This will trigger
693 // onPreviewTextureCopied.
694 // TODO: Need to animate the camera switch
695 switchCamera();
696 }
697 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800698
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800699 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100700 new ButtonManager.ButtonCallback() {
701 @Override
702 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700703 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100704 if (GcamHelper.hasGcamCapture()) {
705 // Set the camera setting to default backfacing.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700706 settingsManager.setToDefault(mAppController.getModuleScope(),
707 Keys.KEY_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700708 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100709 } else {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700710 if (Keys.isHdrOn(settingsManager)) {
Angus Kong831347d2014-06-16 16:07:28 -0700711 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
712 mCameraCapabilities.getStringifier().stringify(
713 CameraCapabilities.SceneMode.HDR));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700714 } else {
Angus Kong831347d2014-06-16 16:07:28 -0700715 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
716 mCameraCapabilities.getStringifier().stringify(
717 CameraCapabilities.SceneMode.AUTO));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700718 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100719 updateParametersSceneMode();
Angus Kong6607dae2014-06-10 16:07:45 -0700720 mCameraDevice.applySettings(mCameraSettings);
Doris Liu8ad8ad42014-03-27 14:43:31 -0700721 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100722 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800723 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100724 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800725
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800726 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
727 @Override
728 public void onClick(View v) {
729 onCaptureCancelled();
730 }
731 };
732
733 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
734 @Override
735 public void onClick(View v) {
736 onCaptureDone();
737 }
738 };
739
740 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
741 @Override
742 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800743 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800744 onCaptureRetake();
745 }
746 };
747
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800748 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700749 public void hardResetSettings(SettingsManager settingsManager) {
Erin Dahlgrene1547932014-06-23 15:17:07 -0700750 // PhotoModule should hard reset HDR+ to off,
751 // and HDR to off if HDR+ is supported.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700752 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
Erin Dahlgrene1547932014-06-23 15:17:07 -0700753 if (GcamHelper.hasGcamCapture()) {
Erin Dahlgren21a62362014-06-24 10:13:01 -0700754 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
Erin Dahlgrene1547932014-06-23 15:17:07 -0700755 }
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700756 }
757
758 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800759 public HardwareSpec getHardwareSpec() {
Angus Kong831347d2014-06-16 16:07:28 -0700760 return (mCameraSettings != null ?
761 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800762 }
763
764 @Override
765 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
766 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
767
768 bottomBarSpec.enableCamera = true;
769 bottomBarSpec.cameraCallback = mCameraCallback;
Erin Dahlgren6190c362014-06-13 14:12:08 -0700770 bottomBarSpec.enableFlash = !mAppController.getSettingsManager()
771 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800772 bottomBarSpec.enableHdr = true;
773 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800774 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700775 if (mCameraCapabilities != null) {
776 bottomBarSpec.enableExposureCompensation = true;
777 bottomBarSpec.exposureCompensationSetCallback =
778 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
779 @Override
780 public void setExposure(int value) {
781 setExposureCompensation(value);
782 }
783 };
784 bottomBarSpec.minExposureCompensation =
785 mCameraCapabilities.getMinExposureCompensation();
786 bottomBarSpec.maxExposureCompensation =
787 mCameraCapabilities.getMaxExposureCompensation();
788 bottomBarSpec.exposureCompensationStep =
789 mCameraCapabilities.getExposureCompensationStep();
790 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800791
Doris Liu6c751642014-05-05 18:43:26 -0700792 bottomBarSpec.enableSelfTimer = true;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700793 bottomBarSpec.showSelfTimer = true;
Doris Liu6c751642014-05-05 18:43:26 -0700794
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800795 if (isImageCaptureIntent()) {
796 bottomBarSpec.showCancel = true;
797 bottomBarSpec.cancelCallback = mCancelCallback;
798 bottomBarSpec.showDone = true;
799 bottomBarSpec.doneCallback = mDoneCallback;
800 bottomBarSpec.showRetake = true;
801 bottomBarSpec.retakeCallback = mRetakeCallback;
802 }
803
804 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800805 }
806
Michael Kolbd6954f32013-03-08 20:43:01 -0800807 // either open a new camera or switch cameras
808 private void openCameraCommon() {
Angus Kong6607dae2014-06-10 16:07:45 -0700809 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
Angus Kong0fb819b2013-10-08 13:44:19 -0700810 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800811 // Set hdr plus to default: off.
812 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700813 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL,
814 Keys.KEY_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700815 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800816 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800817 }
818
ztenghui7b265a62013-09-09 14:58:44 -0700819 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800820 public void updatePreviewAspectRatio(float aspectRatio) {
821 mAppController.updatePreviewAspectRatio(aspectRatio);
822 }
823
Michael Kolb8872c232013-01-29 10:33:22 -0800824 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800825 SettingsManager settingsManager = mActivity.getSettingsManager();
826 if (settingsManager == null) {
827 Log.e(TAG, "Settings manager is null!");
828 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800829 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700830 settingsManager.setToDefault(mAppController.getCameraScope(),
831 Keys.KEY_EXPOSURE);
Michael Kolb8872c232013-01-29 10:33:22 -0800832 }
833
Michael Kolb8872c232013-01-29 10:33:22 -0800834 // Snapshots can only be taken after this is called. It should be called
835 // once only. We could have done these things in onCreate() but we want to
836 // make preview screen appear as soon as possible.
837 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700838 if (mFirstTimeInitialized || mPaused) {
839 return;
840 }
Michael Kolb8872c232013-01-29 10:33:22 -0800841
Michael Kolbd6954f32013-03-08 20:43:01 -0800842 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800843
Angus Kong86d36312013-01-31 18:22:44 -0800844 // We set the listener only when both service and shutterbutton
845 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800846 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800847
Michael Kolb8872c232013-01-29 10:33:22 -0800848 mNamedImages = new NamedImages();
849
850 mFirstTimeInitialized = true;
851 addIdleHandler();
852
Spike Spraguee6374b72014-04-25 17:24:32 -0700853 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800854 }
855
Michael Kolbd6954f32013-03-08 20:43:01 -0800856 // If the activity is paused and resumed, this method will be called in
857 // onResume.
858 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800859 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800860 mNamedImages = new NamedImages();
Angus Kong6607dae2014-06-10 16:07:45 -0700861 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
Michael Kolbd6954f32013-03-08 20:43:01 -0800862 }
863
Michael Kolb8872c232013-01-29 10:33:22 -0800864 private void addIdleHandler() {
865 MessageQueue queue = Looper.myQueue();
866 queue.addIdleHandler(new MessageQueue.IdleHandler() {
867 @Override
868 public boolean queueIdle() {
869 Storage.ensureOSXCompatible();
870 return false;
871 }
872 });
873 }
874
Michael Kolb8872c232013-01-29 10:33:22 -0800875 @Override
876 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800877 if (mFaceDetectionStarted) {
878 return;
879 }
Angus Kong88289042014-04-22 16:39:42 -0700880 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800881 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700882 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700883 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800884 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700885 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800886 }
887 }
888
Michael Kolb8872c232013-01-29 10:33:22 -0800889 @Override
890 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800891 if (!mFaceDetectionStarted) {
892 return;
893 }
Angus Kong88289042014-04-22 16:39:42 -0700894 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800895 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700896 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800897 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800898 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700899 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800900 }
901 }
902
Michael Kolb8872c232013-01-29 10:33:22 -0800903 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700904 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700905
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800906 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700907
Sascha Haeberling37f36112013-08-06 14:31:52 -0700908 public ShutterCallback(boolean needsAnimation) {
909 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700910 }
911
Michael Kolb8872c232013-01-29 10:33:22 -0800912 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700913 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800914 mShutterCallbackTime = System.currentTimeMillis();
915 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
916 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700917 if (mNeedsAnimation) {
918 mActivity.runOnUiThread(new Runnable() {
919 @Override
920 public void run() {
921 animateAfterShutter();
922 }
923 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700924 }
Michael Kolb8872c232013-01-29 10:33:22 -0800925 }
926 }
927
Angus Kong9ef99252013-07-18 18:04:19 -0700928 private final class PostViewPictureCallback
929 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800930 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800931 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800932 mPostViewPictureCallbackTime = System.currentTimeMillis();
933 Log.v(TAG, "mShutterToPostViewCallbackTime = "
934 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
935 + "ms");
936 }
937 }
938
Angus Kong9ef99252013-07-18 18:04:19 -0700939 private final class RawPictureCallback
940 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800941 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800942 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800943 mRawPictureCallbackTime = System.currentTimeMillis();
944 Log.v(TAG, "mShutterToRawCallbackTime = "
945 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
946 }
947 }
948
Angus Kong454d63f2014-05-06 14:45:59 -0700949 private static class ResizeBundle {
950 byte[] jpegData;
951 float targetAspectRatio;
952 ExifInterface exif;
953 }
954
955 /**
956 * @return Cropped image if the target aspect ratio is larger than the jpeg
957 * aspect ratio on the long axis. The original jpeg otherwise.
958 */
959 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
960
961 final byte[] jpegData = dataBundle.jpegData;
962 final ExifInterface exif = dataBundle.exif;
963 float targetAspectRatio = dataBundle.targetAspectRatio;
964
965 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
966 int originalWidth = original.getWidth();
967 int originalHeight = original.getHeight();
968 int newWidth;
969 int newHeight;
970
971 if (originalWidth > originalHeight) {
972 newHeight = (int) (originalWidth / targetAspectRatio);
973 newWidth = originalWidth;
974 } else {
975 newWidth = (int) (originalHeight / targetAspectRatio);
976 newHeight = originalHeight;
977 }
978 int xOffset = (originalWidth - newWidth)/2;
979 int yOffset = (originalHeight - newHeight)/2;
980
981 if (xOffset < 0 || yOffset < 0) {
982 return dataBundle;
983 }
984
985 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
986 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
987 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
988
989 ByteArrayOutputStream stream = new ByteArrayOutputStream();
990
991 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
992 dataBundle.jpegData = stream.toByteArray();
993 return dataBundle;
994 }
995
Angus Kong9ef99252013-07-18 18:04:19 -0700996 private final class JpegPictureCallback
997 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800998 Location mLocation;
999
1000 public JpegPictureCallback(Location loc) {
1001 mLocation = loc;
1002 }
1003
1004 @Override
Angus Kong454d63f2014-05-06 14:45:59 -07001005 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07001006 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001007 if (mPaused) {
1008 return;
1009 }
Doris Liu6432cd62013-06-13 17:20:31 -07001010 if (mIsImageCaptureIntent) {
1011 stopPreview();
1012 }
Angus Kong6607dae2014-06-10 16:07:45 -07001013 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001014 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001015 }
1016
1017 mJpegPictureCallbackTime = System.currentTimeMillis();
1018 // If postview callback has arrived, the captured image is displayed
1019 // in postview callback. If not, the captured image is displayed in
1020 // raw picture callback.
1021 if (mPostViewPictureCallbackTime != 0) {
1022 mShutterToPictureDisplayedTime =
1023 mPostViewPictureCallbackTime - mShutterCallbackTime;
1024 mPictureDisplayedToJpegCallbackTime =
1025 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1026 } else {
1027 mShutterToPictureDisplayedTime =
1028 mRawPictureCallbackTime - mShutterCallbackTime;
1029 mPictureDisplayedToJpegCallbackTime =
1030 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1031 }
1032 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1033 + mPictureDisplayedToJpegCallbackTime + "ms");
1034
Michael Kolb8872c232013-01-29 10:33:22 -08001035 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1036 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001037 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001038 }
1039
Angus Kong454d63f2014-05-06 14:45:59 -07001040 long now = System.currentTimeMillis();
1041 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1042 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1043 mJpegPictureCallbackTime = 0;
1044
1045 final ExifInterface exif = Exif.getExif(originalJpegData);
1046
1047 if (mShouldResizeTo16x9) {
1048 final ResizeBundle dataBundle = new ResizeBundle();
1049 dataBundle.jpegData = originalJpegData;
1050 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1051 dataBundle.exif = exif;
1052 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1053
1054 @Override
1055 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1056 return cropJpegDataToAspectRatio(resizeBundles[0]);
1057 }
1058
1059 @Override
1060 protected void onPostExecute(ResizeBundle result) {
1061 saveFinalPhoto(result.jpegData, result.exif, camera);
1062 }
1063 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1064
1065 } else {
1066 saveFinalPhoto(originalJpegData, exif, camera);
1067 }
1068 }
1069
1070 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1071
Doris Liu36e56fb2013-09-11 17:38:08 -07001072 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001073
Andy Huiberse08bc042014-04-11 19:26:47 -07001074 float zoomValue = 0f;
Angus Kong88289042014-04-22 16:39:42 -07001075 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Angus Kong6607dae2014-06-10 16:07:45 -07001076 int zoomIndex = mCameraSettings.getCurrentZoomIndex();
1077 List<Integer> zoomRatios = mCameraCapabilities.getZoomRatioList();
Andy Huiberse08bc042014-04-11 19:26:47 -07001078 if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
1079 zoomValue = 0.01f * zoomRatios.get(zoomIndex);
1080 }
1081 }
Angus Kong454d63f2014-05-06 14:45:59 -07001082
Angus Kong6607dae2014-06-10 16:07:45 -07001083 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
Andy Huibers203abe52014-05-19 13:59:01 -07001084 String flashSetting =
Erin Dahlgren6190c362014-06-13 14:12:08 -07001085 mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
1086 Keys.KEY_FLASH_MODE);
1087 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager());
Andy Huibers10c58162014-03-29 14:06:54 -07001088 UsageStatistics.instance().photoCaptureDoneEvent(
1089 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1090 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers547d7c82014-05-20 23:03:18 -07001091 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001092 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1093 mShutterTouchCoordinate = null;
1094 mVolumeButtonClickedFlag = false;
Ruben Brunkd217ed02013-10-08 23:31:13 -07001095
Ruben Brunkd7488272013-10-10 18:45:53 -07001096 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001097 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001098 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1099 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001100 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001101 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1102 width = exifWidth;
1103 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001104 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001105 Size s;
Angus Kong6607dae2014-06-10 16:07:45 -07001106 s = mCameraSettings.getCurrentPhotoSize();
Angus Kong454d63f2014-05-06 14:45:59 -07001107 if ((mJpegRotation + orientation) % 180 == 0) {
1108 width = s.width();
1109 height = s.height();
1110 } else {
1111 width = s.height();
1112 height = s.width();
1113 }
Michael Kolb8872c232013-01-29 10:33:22 -08001114 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001115 NamedEntity name = mNamedImages.getNextNameEntity();
1116 String title = (name == null) ? null : name.title;
1117 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001118
1119 // Handle debug mode outputs
1120 if (mDebugUri != null) {
1121 // If using a debug uri, save jpeg there.
1122 saveToDebugUri(jpegData);
1123
1124 // Adjust the title of the debug image shown in mediastore.
1125 if (title != null) {
1126 title = DEBUG_IMAGE_PREFIX + title;
1127 }
1128 }
1129
Michael Kolb8872c232013-01-29 10:33:22 -08001130 if (title == null) {
1131 Log.e(TAG, "Unbalanced name/data pair");
1132 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001133 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001134 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001135 }
Angus Kong0d00a892013-03-26 11:40:40 -07001136 if (mHeading >= 0) {
1137 // heading direction has been updated by the sensor.
1138 ExifTag directionRefTag = exif.buildTag(
1139 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1140 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1141 ExifTag directionTag = exif.buildTag(
1142 ExifInterface.TAG_GPS_IMG_DIRECTION,
1143 new Rational(mHeading, 1));
1144 exif.setTag(directionRefTag);
1145 exif.setTag(directionTag);
1146 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001147 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001148 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001149 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001150 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001151 // Animate capture with real jpeg data instead of a preview
1152 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001153 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001154 } else {
1155 mJpegImageData = jpegData;
1156 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001157 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001158 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001159 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001160 }
1161 }
1162
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001163 // Send the taken photo to remote shutter listeners, if any are
1164 // registered.
Sascha Haeberling3c6de142014-07-14 12:23:36 -07001165 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001166
Michael Kolb8872c232013-01-29 10:33:22 -08001167 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001168 // latency. It's true that someone else could write to the SD card
1169 // in the mean time and fill it, but that could have happened
1170 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001171 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001172 }
1173 }
1174
Angus Kong9ef99252013-07-18 18:04:19 -07001175 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001176 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001177 public void onAutoFocus(boolean focused, CameraProxy camera) {
1178 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001179 if (mPaused) {
1180 return;
1181 }
Michael Kolb8872c232013-01-29 10:33:22 -08001182
1183 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001184 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001185 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001186 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001187 }
1188 }
1189
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001190 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001191 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001192 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001193 @Override
1194 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001195 boolean moving, CameraProxy camera) {
1196 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001197 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001198 }
1199 }
1200
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001201 /**
1202 * This class is just a thread-safe queue for name,date holder objects.
1203 */
1204 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001205 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001206
1207 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001208 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001209 }
1210
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001211 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001212 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001213 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001214 r.date = date;
1215 mQueue.add(r);
1216 }
1217
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001218 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001219 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001220 if (!mQueue.isEmpty()) {
1221 return mQueue.remove(0);
1222 }
Michael Kolb8872c232013-01-29 10:33:22 -08001223 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001224 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001225 }
1226
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001227 public static class NamedEntity {
1228 public String title;
1229 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001230 }
1231 }
1232
1233 private void setCameraState(int state) {
1234 mCameraState = state;
1235 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001236 case PREVIEW_STOPPED:
1237 case SNAPSHOT_IN_PROGRESS:
1238 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001239 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001240 break;
1241 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001242 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001243 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001244 }
1245 }
1246
Sascha Haeberling37f36112013-08-06 14:31:52 -07001247 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001248 // Only animate when in full screen capture mode
1249 // i.e. If monkey/a user swipes to the gallery during picture taking,
1250 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001251 if (!mIsImageCaptureIntent) {
1252 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001253 }
Michael Kolb8872c232013-01-29 10:33:22 -08001254 }
1255
1256 @Override
1257 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001258 // If we are already in the middle of taking a snapshot or the image
1259 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001260 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001261 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001262 return false;
1263 }
1264 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001265
Michael Kolb8872c232013-01-29 10:33:22 -08001266 mPostViewPictureCallbackTime = 0;
1267 mJpegImageData = null;
1268
Angus Kong6607dae2014-06-10 16:07:45 -07001269 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001270
1271 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001272 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001273 }
1274
1275 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -08001276 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001277
Doris Liu3cf565c2013-02-15 10:55:37 -08001278 if (mActivity.isAutoRotateScreen()) {
Sol Boucher73ec9852014-07-24 23:59:21 -07001279 orientation = mDisplayRotation;
Doris Liu3cf565c2013-02-15 10:55:37 -08001280 } else {
1281 orientation = mOrientation;
1282 }
Sol Boucher43e18132014-06-19 15:03:18 -07001283 Characteristics info =
1284 mActivity.getCameraProvider().getCharacteristics(mCameraId);
Sol Boucher73ec9852014-07-24 23:59:21 -07001285 mJpegRotation = info.getJpegOrientation(orientation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001286 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kong6607dae2014-06-10 16:07:45 -07001287 CameraUtil.setGpsParameters(mCameraSettings, loc);
1288 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb8872c232013-01-29 10:33:22 -08001289
Sascha Haeberling88901942013-08-28 17:49:00 -07001290 // We don't want user to press the button again while taking a
1291 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001292 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001293 mCameraDevice.takePicture(mHandler,
1294 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001295 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001296 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001297
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001298 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001299
1300 mFaceDetectionStarted = false;
1301 setCameraState(SNAPSHOT_IN_PROGRESS);
1302 return true;
1303 }
1304
1305 @Override
1306 public void setFocusParameters() {
1307 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1308 }
1309
Michael Kolbd6954f32013-03-08 20:43:01 -08001310 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001311 // If scene mode is set, we cannot set flash mode, white balance, and
1312 // focus mode, instead, we read it from driver
Angus Kong6607dae2014-06-10 16:07:45 -07001313 if (CameraCapabilities.SceneMode.AUTO != mSceneMode) {
1314 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
1315 mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001316 }
1317 }
1318
Angus Kong6607dae2014-06-10 16:07:45 -07001319 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
1320 CameraCapabilities.FocusMode focusMode) {
1321 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrene419b192013-12-03 13:10:27 -08001322 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07001323 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
1324 stringifier.stringify(flashMode));
1325 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
1326 stringifier.stringify(focusMode));
Michael Kolb8872c232013-01-29 10:33:22 -08001327 }
1328
1329 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001330 public void onOrientationChanged(int orientation) {
1331 // We keep the last known orientation. So if the user first orient
1332 // the camera then point the camera to floor or sky, we still have
1333 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001334 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1335 return;
1336 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001337 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001338 }
1339
1340 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001341 public void onCameraAvailable(CameraProxy cameraProxy) {
1342 if (mPaused) {
1343 return;
1344 }
1345 mCameraDevice = cameraProxy;
1346
1347 initializeCapabilities();
1348
1349 // Reset zoom value index.
1350 mZoomValue = 0;
1351 if (mFocusManager == null) {
1352 initializeFocusManager();
1353 }
Angus Kong831347d2014-06-16 16:07:28 -07001354 mFocusManager.updateCapabilities(mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001355
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001356 // Do camera parameter dependent initialization.
Angus Kong6607dae2014-06-10 16:07:45 -07001357 mCameraSettings = mCameraDevice.getSettings();
Angus Kong20fad242013-11-11 18:23:46 -08001358 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001359 // Set a listener which updates camera parameters based
1360 // on changed settings.
1361 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001362 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001363 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001364
Angus Kong20fad242013-11-11 18:23:46 -08001365 startPreview();
1366
1367 onCameraOpened();
1368 }
1369
1370 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001371 public void onCaptureCancelled() {
1372 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1373 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001374 }
1375
Michael Kolbd6954f32013-03-08 20:43:01 -08001376 @Override
1377 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001378 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001379 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001380 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001381 mUI.hidePostCaptureAlert();
Spike Spragueb4a22222014-05-22 14:40:53 -07001382 mUI.hideIntentReviewImageView();
Michael Kolb8872c232013-01-29 10:33:22 -08001383 setupPreview();
1384 }
1385
Michael Kolbd6954f32013-03-08 20:43:01 -08001386 @Override
1387 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001388 if (mPaused) {
1389 return;
1390 }
1391
1392 byte[] data = mJpegImageData;
1393
1394 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001395 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001396 // caller specifies a "save uri" then write the data to its
1397 // stream. Otherwise, pass back a scaled down version of the bitmap
1398 // directly in the extras.
1399 if (mSaveUri != null) {
1400 OutputStream outputStream = null;
1401 try {
1402 outputStream = mContentResolver.openOutputStream(mSaveUri);
1403 outputStream.write(data);
1404 outputStream.close();
1405
1406 mActivity.setResultEx(Activity.RESULT_OK);
1407 mActivity.finish();
1408 } catch (IOException ex) {
1409 // ignore exception
1410 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001411 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001412 }
1413 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001414 ExifInterface exif = Exif.getExif(data);
1415 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001416 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1417 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001418 mActivity.setResultEx(Activity.RESULT_OK,
1419 new Intent("inline-data").putExtra("data", bitmap));
1420 mActivity.finish();
1421 }
1422 } else {
1423 // Save the image to a temp file and invoke the cropper
1424 Uri tempUri = null;
1425 FileOutputStream tempStream = null;
1426 try {
1427 File path = mActivity.getFileStreamPath(sTempCropFilename);
1428 path.delete();
1429 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1430 tempStream.write(data);
1431 tempStream.close();
1432 tempUri = Uri.fromFile(path);
1433 } catch (FileNotFoundException ex) {
1434 mActivity.setResultEx(Activity.RESULT_CANCELED);
1435 mActivity.finish();
1436 return;
1437 } catch (IOException ex) {
1438 mActivity.setResultEx(Activity.RESULT_CANCELED);
1439 mActivity.finish();
1440 return;
1441 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001442 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001443 }
1444
1445 Bundle newExtras = new Bundle();
1446 if (mCropValue.equals("circle")) {
1447 newExtras.putString("circleCrop", "true");
1448 }
1449 if (mSaveUri != null) {
1450 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1451 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001452 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001453 }
1454 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001455 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001456 }
1457
Sascha Haeberling37f36112013-08-06 14:31:52 -07001458 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001459 final String CROP_ACTION = "com.android.camera.action.CROP";
1460 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001461
1462 cropIntent.setData(tempUri);
1463 cropIntent.putExtras(newExtras);
1464
1465 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1466 }
1467 }
1468
Michael Kolb8872c232013-01-29 10:33:22 -08001469 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001470 public void onShutterCoordinate(TouchCoordinate coord) {
1471 mShutterTouchCoordinate = coord;
1472 }
1473
1474 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001475 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001476 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001477 }
1478
1479 @Override
1480 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001481 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001482 || (mCameraState == PREVIEW_STOPPED)) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001483 mVolumeButtonClickedFlag = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001484 return;
1485 }
Michael Kolb8872c232013-01-29 10:33:22 -08001486
1487 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001488 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001489 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001490 + mActivity.getStorageSpaceBytes());
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001491 mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001492 return;
1493 }
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001494 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1495 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
Michael Kolb8872c232013-01-29 10:33:22 -08001496
Erin Dahlgren6190c362014-06-13 14:12:08 -07001497 int countDownDuration = mActivity.getSettingsManager()
1498 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
Andy Huibers547d7c82014-05-20 23:03:18 -07001499 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001500 if (countDownDuration > 0) {
1501 // Start count down.
1502 mAppController.getCameraAppUI().transitionToCancel();
1503 mAppController.getCameraAppUI().hideModeOptions();
1504 mUI.startCountdown(countDownDuration);
1505 return;
1506 } else {
1507 focusAndCapture();
1508 }
1509 }
1510
1511 private void focusAndCapture() {
Angus Kong6607dae2014-06-10 16:07:45 -07001512 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001513 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001514 }
Michael Kolb8872c232013-01-29 10:33:22 -08001515 // If the user wants to do a snapshot while the previous one is still
1516 // in progress, remember the fact and do it after we finish the previous
1517 // one and re-start the preview. Snapshot in progress also includes the
1518 // state that autofocus is focusing and a picture will be taken when
1519 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001520 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1521 if (!mIsImageCaptureIntent) {
1522 mSnapshotOnIdle = true;
1523 }
Michael Kolb8872c232013-01-29 10:33:22 -08001524 return;
1525 }
1526
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001527 mSnapshotOnIdle = false;
Angus Kong831347d2014-06-16 16:07:28 -07001528 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001529 }
1530
Doris Liu6c751642014-05-05 18:43:26 -07001531 @Override
1532 public void onRemainingSecondsChanged(int remainingSeconds) {
1533 mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
1534 }
1535
1536 @Override
1537 public void onCountDownFinished() {
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001538 if (mIsImageCaptureIntent) {
1539 mAppController.getCameraAppUI().transitionToIntentReviewLayout();
1540 } else {
1541 mAppController.getCameraAppUI().transitionToCapture();
1542 }
Doris Liu6c751642014-05-05 18:43:26 -07001543 mAppController.getCameraAppUI().showModeOptions();
1544 if (mPaused) {
1545 return;
1546 }
1547 focusAndCapture();
1548 }
1549
Andy Huibersdef975d2013-11-22 09:13:39 -08001550 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001551 if (mPaused) {
1552 return;
1553 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001554 Log.v(TAG, "Executing onResumeTasks.");
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001555
1556 mCountdownSoundPlayer.loadSounds();
1557 if (mFocusManager != null) {
1558 // If camera is not open when resume is called, focus manager will
1559 // not be initialized yet, in which case it will start listening to
1560 // preview area size change later in the initialization.
1561 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1562 }
1563 mAppController.addPreviewAreaSizeChangedListener(mUI);
1564
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001565 CameraProvider camProvider = mActivity.getCameraProvider();
1566 if (camProvider == null) {
1567 // No camera provider, the Activity is destroyed already.
1568 return;
1569 }
1570 camProvider.requestCamera(mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -08001571
Michael Kolb8872c232013-01-29 10:33:22 -08001572 mJpegPictureCallbackTime = 0;
1573 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001574
1575 mOnResumeTime = SystemClock.uptimeMillis();
1576 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001577
1578 // If first time initialization is not finished, put it in the
1579 // message queue.
1580 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001581 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001582 } else {
1583 initializeSecondTime();
1584 }
Michael Kolb8872c232013-01-29 10:33:22 -08001585
Angus Kong0d00a892013-03-26 11:40:40 -07001586 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1587 if (gsensor != null) {
1588 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1589 }
1590
1591 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1592 if (msensor != null) {
1593 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1594 }
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001595
1596 getServices().getRemoteShutterListener().onModuleReady(this);
1597 SessionStatsCollector.instance().sessionActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001598 }
1599
Angus Kongc4e66562013-11-22 23:03:21 -08001600 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001601 * @return Whether the currently active camera is front-facing.
1602 */
1603 private boolean isCameraFrontFacing() {
Sol Boucher43e18132014-06-19 15:03:18 -07001604 return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1605 .isFacingFront();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001606 }
1607
1608 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001609 * The focus manager is the first UI related element to get initialized, and
1610 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001611 */
1612 private void initializeFocusManager() {
1613 // Create FocusManager object. startPreview needs it.
1614 // if mFocusManager not null, reuse it
1615 // otherwise create a new instance
1616 if (mFocusManager != null) {
1617 mFocusManager.removeMessages();
1618 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001619 mMirror = isCameraFrontFacing();
Angus Kong831347d2014-06-16 16:07:28 -07001620 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
Angus Kongc4e66562013-11-22 23:03:21 -08001621 R.array.pref_camera_focusmode_default_array);
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -07001622 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1623 new ArrayList<CameraCapabilities.FocusMode>();
Angus Kong831347d2014-06-16 16:07:28 -07001624 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1625 for (String modeString : defaultFocusModesStrings) {
1626 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1627 if (mode != null) {
1628 defaultFocusModes.add(mode);
1629 }
1630 }
1631 mFocusManager =
1632 new FocusOverlayManager(mAppController, defaultFocusModes,
1633 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
1634 mUI.getFocusUI());
Kevin Gabayanfb333362014-06-02 14:48:20 -07001635 MotionManager motionManager = getServices().getMotionManager();
1636 if (motionManager != null) {
1637 motionManager.addListener(mFocusManager);
1638 }
Angus Kongc4e66562013-11-22 23:03:21 -08001639 }
Doris Liu482de022013-12-18 19:18:16 -08001640 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001641 }
1642
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001643 /**
1644 * @return Whether we are resuming from within the lockscreen.
1645 */
1646 private boolean isResumeFromLockscreen() {
1647 String action = mActivity.getIntent().getAction();
1648 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1649 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1650 }
1651
Angus Kong20fad242013-11-11 18:23:46 -08001652 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001653 public void resume() {
1654 mPaused = false;
Doris Liu59401042014-01-14 17:51:32 -08001655
Angus Kongc4e66562013-11-22 23:03:21 -08001656 // Add delay on resume from lock screen only, in order to to speed up
1657 // the onResume --> onPause --> onResume cycle from lock screen.
1658 // Don't do always because letting go of thread can cause delay.
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001659 if (isResumeFromLockscreen()) {
Angus Kongc4e66562013-11-22 23:03:21 -08001660 Log.v(TAG, "On resume, from lock screen.");
1661 // Note: onPauseAfterSuper() will delete this runnable, so we will
1662 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001663 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001664 } else {
1665 Log.v(TAG, "On resume.");
1666 onResumeTasks();
1667 }
1668 }
1669
1670 @Override
1671 public void pause() {
Michael Kolb8872c232013-01-29 10:33:22 -08001672 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001673 mHandler.removeCallbacks(mResumeTaskRunnable);
Sascha Haeberling1bf08892014-06-18 09:38:06 -07001674 getServices().getRemoteShutterListener().onModuleExit();
Andy Huibers10c58162014-03-29 14:06:54 -07001675 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001676
Angus Kong0d00a892013-03-26 11:40:40 -07001677 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1678 if (gsensor != null) {
1679 mSensorManager.unregisterListener(this, gsensor);
1680 }
1681
1682 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1683 if (msensor != null) {
1684 mSensorManager.unregisterListener(this, msensor);
1685 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001686
Michael Kolb8872c232013-01-29 10:33:22 -08001687 // Reset the focus first. Camera CTS does not guarantee that
1688 // cancelAutoFocus is allowed after preview stops.
1689 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1690 mCameraDevice.cancelAutoFocus();
1691 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001692
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001693 // If the camera has not been opened asynchronously yet,
1694 // and startPreview hasn't been called, then this is a no-op.
1695 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001696 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001697 cancelCountDown();
1698 mCountdownSoundPlayer.release();
Michael Kolb8872c232013-01-29 10:33:22 -08001699
Angus Kongce5480e2013-01-29 17:43:48 -08001700 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001701 // If we are in an image capture intent and has taken
1702 // a picture, we just clear it in onPause.
1703 mJpegImageData = null;
1704
Angus Kongdcccc512013-08-08 17:06:03 -07001705 // Remove the messages and runnables in the queue.
1706 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001707
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001708 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001709 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001710 mUI.onPause();
1711
Michael Kolb8872c232013-01-29 10:33:22 -08001712 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001713 if (mFocusManager != null) {
1714 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001715 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001716 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001717 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001718 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001719
1720 SettingsManager settingsManager = mActivity.getSettingsManager();
1721 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001722 }
1723
Angus Kong20fad242013-11-11 18:23:46 -08001724 @Override
1725 public void destroy() {
1726 // TODO: implement this.
1727 }
1728
1729 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001730 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001731 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001732 }
1733
1734 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001735 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001736 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001737 setDisplayOrientation();
1738 }
1739 }
1740
Michael Kolb8872c232013-01-29 10:33:22 -08001741 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001742 return isCameraIdle()
1743 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001744 }
1745
1746 @Override
1747 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001748 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001749 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001750 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001751 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001752 setCameraState(FOCUSING);
1753 }
1754
1755 @Override
1756 public void cancelAutoFocus() {
1757 mCameraDevice.cancelAutoFocus();
1758 setCameraState(IDLE);
1759 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1760 }
1761
Michael Kolb8872c232013-01-29 10:33:22 -08001762 @Override
1763 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001764 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1765 || mCameraState == SNAPSHOT_IN_PROGRESS
1766 || mCameraState == SWITCHING_CAMERA
1767 || mCameraState == PREVIEW_STOPPED) {
1768 return;
1769 }
1770
1771 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001772 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1773 return;
1774 }
Doris Liu482de022013-12-18 19:18:16 -08001775 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001776 }
1777
1778 @Override
1779 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001780 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001781 }
1782
1783 @Override
1784 public boolean onKeyDown(int keyCode, KeyEvent event) {
1785 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001786 case KeyEvent.KEYCODE_VOLUME_UP:
1787 case KeyEvent.KEYCODE_VOLUME_DOWN:
1788 case KeyEvent.KEYCODE_FOCUS:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001789 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1790 !mActivity.getCameraAppUI().isInIntentReview()) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001791 if (event.getRepeatCount() == 0) {
1792 onShutterButtonFocus(true);
1793 }
1794 return true;
1795 }
1796 return false;
1797 case KeyEvent.KEYCODE_CAMERA:
1798 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1799 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001800 }
1801 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001802 case KeyEvent.KEYCODE_DPAD_CENTER:
1803 // If we get a dpad center event without any focused view, move
1804 // the focus to the shutter button and press it.
1805 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1806 // Start auto-focus immediately to reduce shutter lag. After
1807 // the shutter button gets the focus, onShutterButtonFocus()
1808 // will be called again but it is fine.
1809 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001810 }
1811 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001812 }
1813 return false;
1814 }
1815
1816 @Override
1817 public boolean onKeyUp(int keyCode, KeyEvent event) {
1818 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001819 case KeyEvent.KEYCODE_VOLUME_UP:
1820 case KeyEvent.KEYCODE_VOLUME_DOWN:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001821 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1822 !mActivity.getCameraAppUI().isInIntentReview()) {
1823 if (mUI.isCountingDown()) {
1824 cancelCountDown();
1825 } else {
1826 mVolumeButtonClickedFlag = true;
1827 onShutterButtonClick();
1828 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001829 return true;
1830 }
1831 return false;
1832 case KeyEvent.KEYCODE_FOCUS:
1833 if (mFirstTimeInitialized) {
1834 onShutterButtonFocus(false);
1835 }
Michael Kolb8872c232013-01-29 10:33:22 -08001836 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001837 }
1838 return false;
1839 }
1840
Michael Kolb8872c232013-01-29 10:33:22 -08001841 private void closeCamera() {
1842 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001843 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001844 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001845 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001846 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001847
Michael Kolb8872c232013-01-29 10:33:22 -08001848 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001849 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001850 mCameraDevice = null;
1851 setCameraState(PREVIEW_STOPPED);
1852 mFocusManager.onCameraReleased();
1853 }
1854 }
1855
1856 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001857 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
Sol Boucher43e18132014-06-19 15:03:18 -07001858 Characteristics info =
1859 mActivity.getCameraProvider().getCharacteristics(mCameraId);
Sol Boucher73ec9852014-07-24 23:59:21 -07001860 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001861 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001862 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001863 if (mFocusManager != null) {
1864 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1865 }
Doris Liu6432cd62013-06-13 17:20:31 -07001866 // Change the camera display orientation
1867 if (mCameraDevice != null) {
Sol Boucher73ec9852014-07-24 23:59:21 -07001868 mCameraDevice.setDisplayOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001869 }
Michael Kolb8872c232013-01-29 10:33:22 -08001870 }
1871
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001872 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001873 private void setupPreview() {
1874 mFocusManager.resetTouchFocus();
1875 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001876 }
1877
Angus Kong20fad242013-11-11 18:23:46 -08001878 /**
1879 * Returns whether we can/should start the preview or not.
1880 */
1881 private boolean checkPreviewPreconditions() {
1882 if (mPaused) {
1883 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001884 }
Michael Kolb8872c232013-01-29 10:33:22 -08001885
Angus Kong20fad242013-11-11 18:23:46 -08001886 if (mCameraDevice == null) {
1887 Log.w(TAG, "startPreview: camera device not ready yet.");
1888 return false;
1889 }
1890
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001891 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001892 if (st == null) {
1893 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001894 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001895 }
1896
1897 if (!mCameraPreviewParamsReady) {
1898 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001899 return false;
1900 }
1901 return true;
1902 }
1903
1904 /**
1905 * The start/stop preview should only run on the UI thread.
1906 */
1907 private void startPreview() {
1908 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001909 return;
1910 }
Angus Kong20fad242013-11-11 18:23:46 -08001911
Angus Kong2bca2102014-03-11 16:27:30 -07001912 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001913 setDisplayOrientation();
1914
1915 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001916 // If the focus mode is continuous autofocus, call cancelAutoFocus
1917 // to resume it because it may have been paused by autoFocus call.
Angus Kong831347d2014-06-16 16:07:28 -07001918 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1919 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001920 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001921 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001922 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1923 }
1924 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001925 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001926
Alan Newbergerd41766f2014-04-09 18:25:34 -07001927 Log.i(TAG, "startPreview");
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001928 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001929
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001930 mFocusManager.onPreviewStarted();
1931 onPreviewStarted();
Andy Huibers10c58162014-03-29 14:06:54 -07001932 SessionStatsCollector.instance().previewActive(true);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001933 if (mSnapshotOnIdle) {
1934 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001935 }
1936 }
1937
Michael Kolbd6954f32013-03-08 20:43:01 -08001938 @Override
1939 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001940 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001941 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001942 mCameraDevice.stopPreview();
1943 mFaceDetectionStarted = false;
1944 }
1945 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001946 if (mFocusManager != null) {
1947 mFocusManager.onPreviewStopped();
1948 }
Andy Huibers10c58162014-03-29 14:06:54 -07001949 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001950 }
1951
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001952 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -07001953 public void onSettingChanged(SettingsManager settingsManager, String key) {
1954 if (key.equals(Keys.KEY_FLASH_MODE)) {
1955 updateParametersFlashMode();
1956 }
1957 if (key.equals(Keys.KEY_CAMERA_HDR)) {
1958 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
1959 Keys.KEY_CAMERA_HDR)) {
1960 // HDR is on.
1961 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
1962 mFlashModeBeforeSceneMode = settingsManager.getString(
1963 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
1964 } else {
1965 if (mFlashModeBeforeSceneMode != null) {
1966 settingsManager.set(mAppController.getCameraScope(),
1967 Keys.KEY_FLASH_MODE,
1968 mFlashModeBeforeSceneMode);
1969 updateParametersFlashMode();
1970 mFlashModeBeforeSceneMode = null;
Angus Kong8c07fe92014-05-22 14:42:45 -07001971 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07001972 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001973 }
1974 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001975
1976 if (mCameraDevice != null) {
Angus Kong6607dae2014-06-10 16:07:45 -07001977 mCameraDevice.applySettings(mCameraSettings);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001978 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001979 }
1980
Michael Kolb8872c232013-01-29 10:33:22 -08001981 private void updateCameraParametersInitialize() {
1982 // Reset preview frame rate to the maximum because it may be lowered by
1983 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07001984 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07001985 if (fpsRange != null && fpsRange.length > 0) {
Angus Kong831347d2014-06-16 16:07:28 -07001986 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
Michael Kolb8872c232013-01-29 10:33:22 -08001987 }
1988
Angus Kong831347d2014-06-16 16:07:28 -07001989 mCameraSettings.setRecordingHintEnabled(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001990
Angus Kong6607dae2014-06-10 16:07:45 -07001991 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
1992 mCameraSettings.setVideoStabilization(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001993 }
1994 }
1995
1996 private void updateCameraParametersZoom() {
1997 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07001998 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Angus Kong6607dae2014-06-10 16:07:45 -07001999 mCameraSettings.setZoomIndex(mZoomValue);
Michael Kolb8872c232013-01-29 10:33:22 -08002000 }
2001 }
2002
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002003 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002004 private void setAutoExposureLockIfSupported() {
2005 if (mAeLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002006 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002007 }
2008 }
2009
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002010 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002011 private void setAutoWhiteBalanceLockIfSupported() {
2012 if (mAwbLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002013 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002014 }
2015 }
2016
Michael Kolb8872c232013-01-29 10:33:22 -08002017 private void setFocusAreasIfSupported() {
2018 if (mFocusAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002019 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002020 }
2021 }
2022
Michael Kolb8872c232013-01-29 10:33:22 -08002023 private void setMeteringAreasIfSupported() {
2024 if (mMeteringAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002025 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002026 }
2027 }
2028
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002029 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08002030 setAutoExposureLockIfSupported();
2031 setAutoWhiteBalanceLockIfSupported();
2032 setFocusAreasIfSupported();
2033 setMeteringAreasIfSupported();
2034
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002035 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07002036 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002037 mCameraSettings
2038 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Andy Huibers10c58162014-03-29 14:06:54 -07002039 SessionStatsCollector.instance().autofocusActive(
Angus Kong831347d2014-06-16 16:07:28 -07002040 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
2041 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
2042 );
Michael Kolbd3253f22013-07-12 11:36:47 -07002043
Michael Kolb8872c232013-01-29 10:33:22 -08002044 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002045 updateParametersPictureSize();
2046
2047 // Set JPEG quality.
2048 updateParametersPictureQuality();
2049
2050 // For the following settings, we need to check if the settings are
2051 // still supported by latest driver, if not, ignore the settings.
2052
2053 // Set exposure compensation
2054 updateParametersExposureCompensation();
2055
2056 // Set the scene mode: also sets flash and white balance.
2057 updateParametersSceneMode();
2058
2059 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2060 updateAutoFocusMoveCallback();
2061 }
2062 }
2063
2064 private void updateParametersPictureSize() {
2065 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002066 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT
2067 : Keys.KEY_PICTURE_SIZE_BACK;
2068 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
2069 pictureSizeKey);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002070
Angus Kong6607dae2014-06-10 16:07:45 -07002071 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Andy Huibers90c7ad52014-05-20 22:13:54 -07002072 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2073 mCameraDevice.getCameraId(), supported);
Angus Kong6607dae2014-06-10 16:07:45 -07002074 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002075 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002076
2077 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2078 mCameraDevice.getCameraId());
2079 if (ApiHelper.IS_NEXUS_5) {
2080 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2081 mShouldResizeTo16x9 = true;
2082 } else {
2083 mShouldResizeTo16x9 = false;
2084 }
2085 }
Michael Kolb8872c232013-01-29 10:33:22 -08002086
2087 // Set a preview size that is closest to the viewfinder height and has
2088 // the right aspect ratio.
Angus Kong6607dae2014-06-10 16:07:45 -07002089 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07002090 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002091 (double) size.width() / size.height());
Angus Kong6607dae2014-06-10 16:07:45 -07002092 Size original = mCameraSettings.getCurrentPreviewSize();
2093 if (!optimalSize.equals(original)) {
Angus Kong6607dae2014-06-10 16:07:45 -07002094 mCameraSettings.setPreviewSize(optimalSize);
Doris Liu6432cd62013-06-13 17:20:31 -07002095
Michael Kolb8872c232013-01-29 10:33:22 -08002096 // Zoom related settings will be changed for different preview
2097 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002098 if (mHandler.getLooper() == Looper.myLooper()) {
2099 // On UI thread only, not when camera starts up
2100 setupPreview();
2101 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002102 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002103 }
Angus Kong6607dae2014-06-10 16:07:45 -07002104 mCameraSettings = mCameraDevice.getSettings();
Michael Kolb8872c232013-01-29 10:33:22 -08002105 }
Doris Liu95405742013-11-05 15:25:26 -08002106
Angus Kong00b7b102014-04-24 15:46:52 -07002107 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2108 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2109 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002110 }
Angus Kong00b7b102014-04-24 15:46:52 -07002111 Log.i(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002112 }
Michael Kolb8872c232013-01-29 10:33:22 -08002113
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002114 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002115 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2116 CameraProfile.QUALITY_HIGH);
Angus Kong6607dae2014-06-10 16:07:45 -07002117 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002118 }
Michael Kolb8872c232013-01-29 10:33:22 -08002119
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002120 private void updateParametersExposureCompensation() {
2121 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002122 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2123 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2124 int value = settingsManager.getInteger(mAppController.getCameraScope(),
2125 Keys.KEY_EXPOSURE);
Angus Kong88289042014-04-22 16:39:42 -07002126 int max = mCameraCapabilities.getMaxExposureCompensation();
2127 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002128 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002129 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002130 } else {
2131 Log.w(TAG, "invalid exposure range: " + value);
2132 }
Michael Kolb8872c232013-01-29 10:33:22 -08002133 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002134 // If exposure compensation is not enabled, reset the exposure compensation value.
2135 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002136 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002137
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002138 }
2139
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002140 private void updateParametersSceneMode() {
Angus Kong6607dae2014-06-10 16:07:45 -07002141 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002142 SettingsManager settingsManager = mActivity.getSettingsManager();
2143
Erin Dahlgren6190c362014-06-13 14:12:08 -07002144 mSceneMode = stringifier.
2145 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2146 Keys.KEY_SCENE_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002147 if (mCameraCapabilities.supports(mSceneMode)) {
2148 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2149 mCameraSettings.setSceneMode(mSceneMode);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002150
2151 // Setting scene mode will change the settings of flash mode,
2152 // white balance, and focus mode. Here we read back the
2153 // parameters, so we can know those settings.
Angus Kong6607dae2014-06-10 16:07:45 -07002154 mCameraDevice.applySettings(mCameraSettings);
2155 mCameraSettings = mCameraDevice.getSettings();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002156 }
2157 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002158 mSceneMode = mCameraSettings.getCurrentSceneMode();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002159 if (mSceneMode == null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002160 mSceneMode = CameraCapabilities.SceneMode.AUTO;
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002161 }
2162 }
2163
Angus Kong6607dae2014-06-10 16:07:45 -07002164 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
Michael Kolb8872c232013-01-29 10:33:22 -08002165 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002166 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002167
Michael Kolb8872c232013-01-29 10:33:22 -08002168 // Set focus mode.
2169 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002170 mCameraSettings.setFocusMode(
2171 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002172 } else {
Angus Kong831347d2014-06-16 16:07:28 -07002173 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08002174 }
Michael Kolb8872c232013-01-29 10:33:22 -08002175 }
2176
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002177 private void updateParametersFlashMode() {
2178 SettingsManager settingsManager = mActivity.getSettingsManager();
2179
Angus Kong6607dae2014-06-10 16:07:45 -07002180 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
Erin Dahlgren6190c362014-06-13 14:12:08 -07002181 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2182 Keys.KEY_FLASH_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002183 if (mCameraCapabilities.supports(flashMode)) {
2184 mCameraSettings.setFlashMode(flashMode);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002185 }
2186 }
2187
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002188 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002189 private void updateAutoFocusMoveCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -07002190 if (mCameraSettings.getCurrentFocusMode() ==
2191 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Angus Kong9ef99252013-07-18 18:04:19 -07002192 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002193 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002194 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002195 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002196 }
2197 }
2198
Spike Spragueabf54e22014-03-27 15:41:28 -07002199 /**
2200 * Sets the exposure compensation to the given value and also updates settings.
2201 *
2202 * @param value exposure compensation value to be set
2203 */
2204 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002205 int max = mCameraCapabilities.getMaxExposureCompensation();
2206 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002207 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002208 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002209 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002210 settingsManager.set(mAppController.getCameraScope(),
2211 Keys.KEY_EXPOSURE, value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002212 } else {
2213 Log.w(TAG, "invalid exposure range: " + value);
2214 }
2215 }
2216
Michael Kolb8872c232013-01-29 10:33:22 -08002217 // We separate the parameters into several subsets, so we can update only
2218 // the subsets actually need updating. The PREFERENCE set needs extra
2219 // locking because the preference can be changed from GLThread as well.
2220 private void setCameraParameters(int updateSet) {
2221 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2222 updateCameraParametersInitialize();
2223 }
2224
2225 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2226 updateCameraParametersZoom();
2227 }
2228
2229 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002230 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002231 }
2232
Angus Kong6607dae2014-06-10 16:07:45 -07002233 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb8872c232013-01-29 10:33:22 -08002234 }
2235
2236 // If the Camera is idle, update the parameters immediately, otherwise
2237 // accumulate them in mUpdateSet and update later.
2238 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2239 mUpdateSet |= additionalUpdateSet;
2240 if (mCameraDevice == null) {
2241 // We will update all the parameters when we open the device, so
2242 // we don't need to do anything now.
2243 mUpdateSet = 0;
2244 return;
2245 } else if (isCameraIdle()) {
2246 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002247 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002248 mUpdateSet = 0;
2249 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002250 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2251 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002252 }
2253 }
2254 }
2255
ztenghui7b265a62013-09-09 14:58:44 -07002256 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002257 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002258 return (mCameraState == IDLE) ||
2259 (mCameraState == PREVIEW_STOPPED) ||
2260 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002261 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002262 }
2263
ztenghui7b265a62013-09-09 14:58:44 -07002264 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002265 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002266 String action = mActivity.getIntent().getAction();
2267 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002268 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002269 }
2270
2271 private void setupCaptureParams() {
2272 Bundle myExtras = mActivity.getIntent().getExtras();
2273 if (myExtras != null) {
2274 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2275 mCropValue = myExtras.getString("crop");
2276 }
2277 }
2278
Michael Kolb8872c232013-01-29 10:33:22 -08002279 private void initializeCapabilities() {
Angus Kong88289042014-04-22 16:39:42 -07002280 mCameraCapabilities = mCameraDevice.getCapabilities();
2281 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2282 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2283 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2284 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kong6607dae2014-06-10 16:07:45 -07002285 mContinuousFocusSupported =
2286 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002287 }
2288
Marco Nelissen0744e4a2013-11-22 01:47:37 +00002289 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08002290 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002291 public int onZoomChanged(int index) {
2292 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002293 if (mPaused) {
2294 return index;
2295 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002296 mZoomValue = index;
Angus Kong6607dae2014-06-10 16:07:45 -07002297 if (mCameraSettings == null || mCameraDevice == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002298 return index;
2299 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002300 // Set zoom parameters asynchronously
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -07002301 mCameraSettings.setZoomRatio(mZoomValue);
Angus Kong6607dae2014-06-10 16:07:45 -07002302 mCameraDevice.applySettings(mCameraSettings);
Angus Kong831347d2014-06-16 16:07:28 -07002303 CameraSettings settings = mCameraDevice.getSettings();
2304 if (settings != null) {
2305 return settings.getCurrentZoomIndex();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002306 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002307 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002308 }
2309
2310 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002311 public int getCameraState() {
2312 return mCameraState;
2313 }
2314
2315 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002316 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002317 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002318 }
Angus Kong86d36312013-01-31 18:22:44 -08002319
2320 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002321 public void onLowMemory() {
2322 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002323 }
Angus Kong0d00a892013-03-26 11:40:40 -07002324
2325 @Override
2326 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2327 }
2328
2329 @Override
2330 public void onSensorChanged(SensorEvent event) {
2331 int type = event.sensor.getType();
2332 float[] data;
2333 if (type == Sensor.TYPE_ACCELEROMETER) {
2334 data = mGData;
2335 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2336 data = mMData;
2337 } else {
2338 // we should not be here.
2339 return;
2340 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002341 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002342 data[i] = event.values[i];
2343 }
2344 float[] orientation = new float[3];
2345 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2346 SensorManager.getOrientation(mR, orientation);
2347 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2348 if (mHeading < 0) {
2349 mHeading += 360;
2350 }
Angus Kong0d00a892013-03-26 11:40:40 -07002351 }
Doris Liu6432cd62013-06-13 17:20:31 -07002352
Ruben Brunkd217ed02013-10-08 23:31:13 -07002353 // For debugging only.
2354 public void setDebugUri(Uri uri) {
2355 mDebugUri = uri;
2356 }
2357
2358 // For debugging only.
2359 private void saveToDebugUri(byte[] data) {
2360 if (mDebugUri != null) {
2361 OutputStream outputStream = null;
2362 try {
2363 outputStream = mContentResolver.openOutputStream(mDebugUri);
2364 outputStream.write(data);
2365 outputStream.close();
2366 } catch (IOException e) {
2367 Log.e(TAG, "Exception while writing debug jpeg file", e);
2368 } finally {
2369 CameraUtil.closeSilently(outputStream);
2370 }
2371 }
2372 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002373
2374 @Override
2375 public void onRemoteShutterPress() {
Sascha Haeberling57d4f742014-06-25 11:05:49 -07002376 mHandler.post(new Runnable() {
2377 @Override
2378 public void run() {
2379 focusAndCapture();
2380 }
2381 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002382 }
Doris Liu6c751642014-05-05 18:43:26 -07002383
2384 /**
2385 * This class manages the loading/releasing/playing of the sounds needed for
2386 * countdown timer.
2387 */
2388 private class CountdownSoundPlayer {
2389 private SoundPool mSoundPool;
2390 private int mBeepOnce;
2391 private int mBeepTwice;
2392
2393 void loadSounds() {
2394 // Load the beeps.
Seth Raphael20e20c92014-05-27 09:45:26 -07002395 if (mSoundPool == null) {
2396 mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2397 mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
2398 mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
2399 }
Doris Liu6c751642014-05-05 18:43:26 -07002400 }
2401
2402 void onRemainingSecondsChanged(int newVal) {
2403 if (mSoundPool == null) {
2404 Log.e(TAG, "Cannot play sound - they have not been loaded.");
2405 return;
2406 }
2407 if (newVal == 1) {
2408 mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
2409 } else if (newVal == 2 || newVal == 3) {
2410 mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
2411 }
2412 }
2413
2414 void release() {
Seth Raphael20e20c92014-05-27 09:45:26 -07002415 if (mSoundPool != null) {
2416 mSoundPool.release();
2417 mSoundPool = null;
2418 }
Doris Liu6c751642014-05-05 18:43:26 -07002419 }
2420 }
Michael Kolb8872c232013-01-29 10:33:22 -08002421}