blob: ea2a4e3ed8d913ec9462bce46ab9dfaeb0616154 [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;
27import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080028import android.hardware.Camera.Parameters;
Angus Kong0d00a892013-03-26 11:40:40 -070029import android.hardware.Sensor;
30import android.hardware.SensorEvent;
31import android.hardware.SensorEventListener;
32import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.location.Location;
Doris Liu6c751642014-05-05 18:43:26 -070034import android.media.AudioManager;
Michael Kolb8872c232013-01-29 10:33:22 -080035import android.media.CameraProfile;
Doris Liu6c751642014-05-05 18:43:26 -070036import android.media.SoundPool;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.net.Uri;
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -070038import android.os.AsyncTask;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070039import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080040import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080041import android.os.Handler;
42import android.os.Looper;
43import android.os.Message;
44import android.os.MessageQueue;
45import android.os.SystemClock;
46import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080047import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080048import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080049import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080050
Sameer Padala2c8cc452013-11-05 18:49:12 -080051import com.android.camera.PhotoModule.NamedImages.NamedEntity;
52import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080053import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070054import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070055import com.android.camera.app.LocationManager;
56import com.android.camera.app.MediaSaver;
57import com.android.camera.app.MemoryManager;
58import com.android.camera.app.MemoryManager.MemoryListener;
Angus Kong88289042014-04-22 16:39:42 -070059import com.android.camera.cameradevice.CameraCapabilities;
Doris Liucb8215e2014-05-19 11:53:32 -070060import com.android.camera.cameradevice.CameraManager;
Angus Kong1045fef2014-04-21 11:17:37 -070061import com.android.camera.cameradevice.CameraManager.CameraAFCallback;
62import com.android.camera.cameradevice.CameraManager.CameraAFMoveCallback;
63import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
64import com.android.camera.cameradevice.CameraManager.CameraProxy;
65import com.android.camera.cameradevice.CameraManager.CameraShutterCallback;
Angus Kong6607dae2014-06-10 16:07:45 -070066import com.android.camera.cameradevice.CameraSettings;
67import com.android.camera.cameradevice.Size;
Angus Kong2bca2102014-03-11 16:27:30 -070068import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070069import com.android.camera.exif.ExifInterface;
70import com.android.camera.exif.ExifTag;
71import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080072import com.android.camera.hardware.HardwareSpec;
73import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080074import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080075import com.android.camera.remote.RemoteCameraModule;
Andy Huibers90c7ad52014-05-20 22:13:54 -070076import com.android.camera.settings.CameraPictureSizesCacher;
Angus Kong454d63f2014-05-06 14:45:59 -070077import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080078import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010079import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070080import com.android.camera.ui.CountDownView;
Angus Kongdcccc512013-08-08 17:06:03 -070081import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070082import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070083import com.android.camera.util.GcamHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070084import com.android.camera.util.SessionStatsCollector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070085import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070086import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070087import com.android.camera2.R;
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;
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -070097import java.text.DecimalFormat;
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
Angus Kong2bca2102014-03-11 16:27:30 -0700112 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
Michael Kolb8872c232013-01-29 10:33:22 -0800113
114 // We number the request code from 1000 to avoid collision with Gallery.
115 private static final int REQUEST_CROP = 1000;
116
Angus Kong13e87c42013-11-25 10:02:47 -0800117 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800118 private static final int MSG_FIRST_TIME_INIT = 1;
119 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800120
121 // The subset of parameters we need to update in setCameraParameters().
122 private static final int UPDATE_PARAM_INITIALIZE = 1;
123 private static final int UPDATE_PARAM_ZOOM = 2;
124 private static final int UPDATE_PARAM_PREFERENCE = 4;
125 private static final int UPDATE_PARAM_ALL = -1;
126
Andy Huibersdef975d2013-11-22 09:13:39 -0800127 // This is the delay before we execute onResume tasks when coming
128 // from the lock screen, to allow time for onPause to execute.
129 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800130
Ruben Brunkd7488272013-10-10 18:45:53 -0700131 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
132
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700133 private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0");
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;
Doris Liu6a8ae812014-05-20 12:43:56 -0700141 private boolean mShouldSetPreviewCallbacks = ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK;
Michael Kolbd6954f32013-03-08 20:43:01 -0800142
143 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800144
Michael Kolb8872c232013-01-29 10:33:22 -0800145 // The activity is going to switch to the specified camera id. This is
146 // needed because texture copy is done in GL thread. -1 means camera is not
147 // switching.
148 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800149
150 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
151 // needed to be updated in mUpdateSet.
152 private int mUpdateSet;
153
154 private static final int SCREEN_DELAY = 2 * 60 * 1000;
155
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800156 private int mZoomValue; // The current zoom value.
Andy Huibers547d7c82014-05-20 23:03:18 -0700157 private int mTimerDuration;
Michael Kolb8872c232013-01-29 10:33:22 -0800158
159 private Parameters mInitialParams;
160 private boolean mFocusAreaSupported;
161 private boolean mMeteringAreaSupported;
162 private boolean mAeLockSupported;
163 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700164 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800165
166 // The degrees of the device rotated clockwise from its natural orientation.
167 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800168
169 private static final String sTempCropFilename = "crop-temp";
170
Michael Kolb8872c232013-01-29 10:33:22 -0800171 private boolean mFaceDetectionStarted = false;
172
Michael Kolb8872c232013-01-29 10:33:22 -0800173 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
174 private String mCropValue;
175 private Uri mSaveUri;
176
Ruben Brunkd217ed02013-10-08 23:31:13 -0700177 private Uri mDebugUri;
178
Angus Kongce5480e2013-01-29 17:43:48 -0800179 // We use a queue to generated names of the images to be used later
180 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800181 private NamedImages mNamedImages;
182
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800183 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800184 @Override
185 public void run() {
186 onShutterButtonClick();
187 }
188 };
189
Michael Kolb8872c232013-01-29 10:33:22 -0800190 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800191 * An unpublished intent flag requesting to return as soon as capturing is
192 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800193 */
194 private static final String EXTRA_QUICK_CAPTURE =
195 "android.intent.extra.quickCapture";
196
197 // The display rotation in degrees. This is only valid when mCameraState is
198 // not PREVIEW_STOPPED.
199 private int mDisplayRotation;
200 // The value for android.hardware.Camera.setDisplayOrientation.
201 private int mCameraDisplayOrientation;
202 // The value for UI components like indicators.
203 private int mDisplayOrientation;
204 // The value for android.hardware.Camera.Parameters.setRotation.
205 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700206 // Indicates whether we are using front camera
207 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800208 private boolean mFirstTimeInitialized;
209 private boolean mIsImageCaptureIntent;
210
Michael Kolb8872c232013-01-29 10:33:22 -0800211 private int mCameraState = PREVIEW_STOPPED;
212 private boolean mSnapshotOnIdle = false;
213
214 private ContentResolver mContentResolver;
215
216 private LocationManager mLocationManager;
Doris Liu2b906b82013-12-10 16:34:08 -0800217 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800218
Michael Kolb8872c232013-01-29 10:33:22 -0800219 private final PostViewPictureCallback mPostViewPictureCallback =
220 new PostViewPictureCallback();
221 private final RawPictureCallback mRawPictureCallback =
222 new RawPictureCallback();
223 private final AutoFocusCallback mAutoFocusCallback =
224 new AutoFocusCallback();
225 private final Object mAutoFocusMoveCallback =
226 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700227 ? new AutoFocusMoveCallback()
228 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800229
230 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
231
232 private long mFocusStartTime;
233 private long mShutterCallbackTime;
234 private long mPostViewPictureCallbackTime;
235 private long mRawPictureCallbackTime;
236 private long mJpegPictureCallbackTime;
237 private long mOnResumeTime;
238 private byte[] mJpegImageData;
239
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();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700382 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700383 SettingsManager.VALUE_ON);
384
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
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100410 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
411 mActivity = activity;
412 // TODO: Need to look at the controller interface to see if we can get
413 // rid of passing in the activity directly.
414 mAppController = mActivity;
415
416 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
417 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800418
419 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800420 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800421
Doris Liudb8f9752014-05-12 15:25:13 -0700422 // TODO: Move this to SettingsManager as a part of upgrade procedure.
423 if (!settingsManager.getBoolean(SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO)) {
424 // Switch to back camera to set aspect ratio.
425 mCameraId = Integer.parseInt(settingsManager
426 .getDefaultCameraIdSetting(activity).getDefault());
427 }
428
Michael Kolb8872c232013-01-29 10:33:22 -0800429 mContentResolver = mActivity.getContentResolver();
430
Michael Kolb8872c232013-01-29 10:33:22 -0800431 // Surface texture is from camera screen nail and startPreview needs it.
432 // This must be done before startPreview.
433 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800434
Angus Kong20fad242013-11-11 18:23:46 -0800435 mActivity.getCameraProvider().requestCamera(mCameraId);
436
Michael Kolb8872c232013-01-29 10:33:22 -0800437 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800438 mLocationManager = mActivity.getLocationManager();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800439 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700440 mUI.setCountdownFinishedListener(this);
441
442 // TODO: Make this a part of app controller API.
443 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
444 cancelButton.setOnClickListener(new View.OnClickListener() {
445 @Override
446 public void onClick(View view) {
447 cancelCountDown();
448 }
449 });
450 }
451
452 private void cancelCountDown() {
453 if (mUI.isCountingDown()) {
454 // Cancel on-going countdown.
455 mUI.cancelCountDown();
456 }
457 mAppController.getCameraAppUI().transitionToCapture();
458 mAppController.getCameraAppUI().showModeOptions();
Michael Kolbd6954f32013-03-08 20:43:01 -0800459 }
460
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800461 @Override
462 public boolean isUsingBottomBar() {
463 return true;
464 }
465
Michael Kolbd6954f32013-03-08 20:43:01 -0800466 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800467 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800468 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800469 setupCaptureParams();
470 }
471 }
472
473 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800474 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800475 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800476 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700477 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800478 }
479
Doris Liudb8f9752014-05-12 15:25:13 -0700480 /**
481 * Prompt the user to pick to record location and choose aspect ratio for the
482 * very first run of camera only.
483 */
484 private void settingsFirstRun() {
485 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100486
Doris Liudb8f9752014-05-12 15:25:13 -0700487 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800488 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800489 }
Doris Liudb8f9752014-05-12 15:25:13 -0700490
491 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION);
492 boolean aspectRatioPrompt = !settingsManager.getBoolean(
493 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO);
494 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800495 return;
496 }
Doris Liudb8f9752014-05-12 15:25:13 -0700497
Michael Kolb8872c232013-01-29 10:33:22 -0800498 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800499 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800500 if (backCameraId == -1) {
501 // If there is no back camera, do not show the prompt.
502 return;
503 }
Doris Liudb8f9752014-05-12 15:25:13 -0700504
505 if (locationPrompt) {
506 // Show both location and aspect ratio selection dialog.
507 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700508 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700509 public void onLocationTaggingSelected(boolean selected) {
510 settingsManager.setLocation(selected, mActivity.getLocationManager());
511 }
512 }, createAspectRatioDialogCallback());
513 } else {
514 // App upgrade. Only show aspect ratio selection.
515 mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
516 }
517 }
518
519 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -0700520 Size currentSize = mCameraSettings.getCurrentPhotoSize();
Doris Liudb8f9752014-05-12 15:25:13 -0700521 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
522 if (aspectRatio < 1f) {
523 aspectRatio = 1 / aspectRatio;
524 }
525 final AspectRatioSelector.AspectRatio currentAspectRatio;
526 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
527 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
528 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
529 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
530 } else {
531 // TODO: Log error and not show dialog.
532 return null;
533 }
534
Angus Kong6607dae2014-06-10 16:07:45 -0700535 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes();
Doris Liudb8f9752014-05-12 15:25:13 -0700536 List<Size> pictureSizes = ResolutionUtil
537 .getDisplayableSizesFromSupported(sizes, true);
538
539 // This logic below finds the largest resolution for each aspect ratio.
540 // TODO: Move this somewhere that can be shared with SettingsActivity
541 int aspectRatio4x3Resolution = 0;
542 int aspectRatio16x9Resolution = 0;
543 Size largestSize4x3 = new Size(0, 0);
544 Size largestSize16x9 = new Size(0, 0);
545 for (Size size : pictureSizes) {
546 float pictureAspectRatio = (float) size.width() / (float) size.height();
547 pictureAspectRatio = pictureAspectRatio < 1 ?
548 1f / pictureAspectRatio : pictureAspectRatio;
549 int resolution = size.width() * size.height();
550 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
551 if (resolution > aspectRatio4x3Resolution) {
552 aspectRatio4x3Resolution = resolution;
553 largestSize4x3 = size;
554 }
555 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
556 if (resolution > aspectRatio16x9Resolution) {
557 aspectRatio16x9Resolution = resolution;
558 largestSize16x9 = size;
559 }
560 }
561 }
Doris Liudb8f9752014-05-12 15:25:13 -0700562
Doris Liu08b0cdd2014-05-13 19:19:55 -0700563 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
564 final Size size4x3ToSelect = largestSize4x3;
565 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700566
Doris Liudb8f9752014-05-12 15:25:13 -0700567 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700568
569 @Override
570 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
571 return currentAspectRatio;
572 }
573
574 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700575 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
576 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700577 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700578 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700579 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
580 largestSize4x3Text);
581 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700582 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700583 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
584 largestSize16x9Text);
585 }
586 mActivity.getSettingsManager().setBoolean(
587 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO, true);
588 if (newAspectRatio != currentAspectRatio) {
Doris Liudb8f9752014-05-12 15:25:13 -0700589 stopPreview();
590 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700591 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
592 } else {
593 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700594 }
595 }
596 };
597 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700598 }
Michael Kolb8872c232013-01-29 10:33:22 -0800599
ztenghui7b265a62013-09-09 14:58:44 -0700600 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700601 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800602 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700603 }
604
605 @Override
606 public void onPreviewUIDestroyed() {
607 if (mCameraDevice == null) {
608 return;
609 }
610 mCameraDevice.setPreviewTexture(null);
611 stopPreview();
612 }
613
Doris Liu1dfe7822013-12-12 00:02:08 -0800614 @Override
615 public void startPreCaptureAnimation() {
616 mAppController.startPreCaptureAnimation();
617 }
618
Michael Kolbd6954f32013-03-08 20:43:01 -0800619 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800620 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800621 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800622 }
623
Michael Kolbd6954f32013-03-08 20:43:01 -0800624 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800625 if (mPaused) {
626 return;
627 }
Doris Liu6c751642014-05-05 18:43:26 -0700628 cancelCountDown();
Doris Liu6809eb82014-05-21 13:16:59 -0700629
630 mAppController.freezeScreenUntilPreviewReady();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800631 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800632
Alan Newbergerd41766f2014-04-09 18:25:34 -0700633 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800634 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800635 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800636 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800637 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800638 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800639 if (mFocusManager != null) {
640 mFocusManager.removeMessages();
641 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800642
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700643 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700644 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700645 // Start switch camera animation. Post a message because
646 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800647 }
648
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800649 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100650 new ButtonManager.ButtonCallback() {
651 @Override
652 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800653 // At the time this callback is fired, the camera id
654 // has be set to the desired camera.
655
Angus Kong97e282a2014-03-04 18:44:49 -0800656 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100657 return;
658 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800659 // If switching to back camera, and HDR+ is still on,
660 // switch back to gcam, otherwise handle callback normally.
661 SettingsManager settingsManager = mActivity.getSettingsManager();
662 if (settingsManager.isCameraBackFacing()) {
663 if (settingsManager.requestsReturnToHdrPlus()) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700664 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800665 return;
666 }
667 }
668
Sascha Haeberlingde303232014-02-07 02:30:53 +0100669 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800670
Alan Newbergerd41766f2014-04-09 18:25:34 -0700671 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100672 // We need to keep a preview frame for the animation before
673 // releasing the camera. This will trigger
674 // onPreviewTextureCopied.
675 // TODO: Need to animate the camera switch
676 switchCamera();
677 }
678 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800679
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800680 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100681 new ButtonManager.ButtonCallback() {
682 @Override
683 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700684 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100685 if (GcamHelper.hasGcamCapture()) {
686 // Set the camera setting to default backfacing.
Sascha Haeberlingde303232014-02-07 02:30:53 +0100687 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700688 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100689 } else {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700690 if (settingsManager.isHdrOn()) {
691 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
692 CameraUtil.SCENE_MODE_HDR);
693 } else {
694 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
695 Parameters.SCENE_MODE_AUTO);
696 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100697 updateParametersSceneMode();
Angus Kong6607dae2014-06-10 16:07:45 -0700698 mCameraDevice.applySettings(mCameraSettings);
Doris Liu8ad8ad42014-03-27 14:43:31 -0700699 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100700 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800701 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100702 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800703
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800704 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
705 @Override
706 public void onClick(View v) {
707 onCaptureCancelled();
708 }
709 };
710
711 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
712 @Override
713 public void onClick(View v) {
714 onCaptureDone();
715 }
716 };
717
718 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
719 @Override
720 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800721 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800722 onCaptureRetake();
723 }
724 };
725
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800726 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700727 public void hardResetSettings(SettingsManager settingsManager) {
728 // PhotoModule should hard reset HDR+ to off.
729 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
730 }
731
732 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800733 public HardwareSpec getHardwareSpec() {
Angus Kong6607dae2014-06-10 16:07:45 -0700734 return (mCameraSettings != null ? new HardwareSpecImpl(mCameraCapabilities) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800735 }
736
737 @Override
738 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
739 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
740
741 bottomBarSpec.enableCamera = true;
742 bottomBarSpec.cameraCallback = mCameraCallback;
Angus Kong8c07fe92014-05-22 14:42:45 -0700743 bottomBarSpec.enableFlash = !SettingsManager.VALUE_ON
744 .equals(mAppController.getSettingsManager()
745 .get(SettingsManager.SETTING_CAMERA_HDR));
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800746 bottomBarSpec.enableHdr = true;
747 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800748 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700749 if (mCameraCapabilities != null) {
750 bottomBarSpec.enableExposureCompensation = true;
751 bottomBarSpec.exposureCompensationSetCallback =
752 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
753 @Override
754 public void setExposure(int value) {
755 setExposureCompensation(value);
756 }
757 };
758 bottomBarSpec.minExposureCompensation =
759 mCameraCapabilities.getMinExposureCompensation();
760 bottomBarSpec.maxExposureCompensation =
761 mCameraCapabilities.getMaxExposureCompensation();
762 bottomBarSpec.exposureCompensationStep =
763 mCameraCapabilities.getExposureCompensationStep();
764 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800765
Doris Liu6c751642014-05-05 18:43:26 -0700766 bottomBarSpec.enableSelfTimer = true;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700767 bottomBarSpec.showSelfTimer = true;
Doris Liu6c751642014-05-05 18:43:26 -0700768
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800769 if (isImageCaptureIntent()) {
770 bottomBarSpec.showCancel = true;
771 bottomBarSpec.cancelCallback = mCancelCallback;
772 bottomBarSpec.showDone = true;
773 bottomBarSpec.doneCallback = mDoneCallback;
774 bottomBarSpec.showRetake = true;
775 bottomBarSpec.retakeCallback = mRetakeCallback;
776 }
777
778 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800779 }
780
Michael Kolbd6954f32013-03-08 20:43:01 -0800781 // either open a new camera or switch cameras
782 private void openCameraCommon() {
Angus Kong6607dae2014-06-10 16:07:45 -0700783 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
Angus Kong0fb819b2013-10-08 13:44:19 -0700784 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800785 // Set hdr plus to default: off.
786 SettingsManager settingsManager = mActivity.getSettingsManager();
787 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700788 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800789 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800790 }
791
ztenghui7b265a62013-09-09 14:58:44 -0700792 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800793 public void updatePreviewAspectRatio(float aspectRatio) {
794 mAppController.updatePreviewAspectRatio(aspectRatio);
795 }
796
Michael Kolb8872c232013-01-29 10:33:22 -0800797 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800798 SettingsManager settingsManager = mActivity.getSettingsManager();
799 if (settingsManager == null) {
800 Log.e(TAG, "Settings manager is null!");
801 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800802 }
Spike Spragueabf54e22014-03-27 15:41:28 -0700803 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
Michael Kolb8872c232013-01-29 10:33:22 -0800804 }
805
Michael Kolb8872c232013-01-29 10:33:22 -0800806 // Snapshots can only be taken after this is called. It should be called
807 // once only. We could have done these things in onCreate() but we want to
808 // make preview screen appear as soon as possible.
809 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700810 if (mFirstTimeInitialized || mPaused) {
811 return;
812 }
Michael Kolb8872c232013-01-29 10:33:22 -0800813
Michael Kolbd6954f32013-03-08 20:43:01 -0800814 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800815
Angus Kong86d36312013-01-31 18:22:44 -0800816 // We set the listener only when both service and shutterbutton
817 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800818 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800819
Michael Kolb8872c232013-01-29 10:33:22 -0800820 mNamedImages = new NamedImages();
821
822 mFirstTimeInitialized = true;
823 addIdleHandler();
824
Spike Spraguee6374b72014-04-25 17:24:32 -0700825 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800826 }
827
Michael Kolbd6954f32013-03-08 20:43:01 -0800828 // If the activity is paused and resumed, this method will be called in
829 // onResume.
830 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800831 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800832 mNamedImages = new NamedImages();
Angus Kong6607dae2014-06-10 16:07:45 -0700833 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
Michael Kolbd6954f32013-03-08 20:43:01 -0800834 }
835
Michael Kolb8872c232013-01-29 10:33:22 -0800836 private void addIdleHandler() {
837 MessageQueue queue = Looper.myQueue();
838 queue.addIdleHandler(new MessageQueue.IdleHandler() {
839 @Override
840 public boolean queueIdle() {
841 Storage.ensureOSXCompatible();
842 return false;
843 }
844 });
845 }
846
Michael Kolb8872c232013-01-29 10:33:22 -0800847 @Override
848 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800849 if (mFaceDetectionStarted) {
850 return;
851 }
Angus Kong88289042014-04-22 16:39:42 -0700852 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800853 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700854 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700855 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800856 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700857 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800858 }
859 }
860
Michael Kolb8872c232013-01-29 10:33:22 -0800861 @Override
862 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800863 if (!mFaceDetectionStarted) {
864 return;
865 }
Angus Kong88289042014-04-22 16:39:42 -0700866 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800867 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700868 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800869 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800870 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700871 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800872 }
873 }
874
Michael Kolb8872c232013-01-29 10:33:22 -0800875 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700876 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700877
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800878 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700879
Sascha Haeberling37f36112013-08-06 14:31:52 -0700880 public ShutterCallback(boolean needsAnimation) {
881 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700882 }
883
Michael Kolb8872c232013-01-29 10:33:22 -0800884 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700885 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800886 mShutterCallbackTime = System.currentTimeMillis();
887 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
888 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700889 if (mNeedsAnimation) {
890 mActivity.runOnUiThread(new Runnable() {
891 @Override
892 public void run() {
893 animateAfterShutter();
894 }
895 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700896 }
Michael Kolb8872c232013-01-29 10:33:22 -0800897 }
898 }
899
Angus Kong9ef99252013-07-18 18:04:19 -0700900 private final class PostViewPictureCallback
901 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800902 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800903 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800904 mPostViewPictureCallbackTime = System.currentTimeMillis();
905 Log.v(TAG, "mShutterToPostViewCallbackTime = "
906 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
907 + "ms");
908 }
909 }
910
Angus Kong9ef99252013-07-18 18:04:19 -0700911 private final class RawPictureCallback
912 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800913 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800914 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800915 mRawPictureCallbackTime = System.currentTimeMillis();
916 Log.v(TAG, "mShutterToRawCallbackTime = "
917 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
918 }
919 }
920
Angus Kong454d63f2014-05-06 14:45:59 -0700921 private static class ResizeBundle {
922 byte[] jpegData;
923 float targetAspectRatio;
924 ExifInterface exif;
925 }
926
927 /**
928 * @return Cropped image if the target aspect ratio is larger than the jpeg
929 * aspect ratio on the long axis. The original jpeg otherwise.
930 */
931 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
932
933 final byte[] jpegData = dataBundle.jpegData;
934 final ExifInterface exif = dataBundle.exif;
935 float targetAspectRatio = dataBundle.targetAspectRatio;
936
937 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
938 int originalWidth = original.getWidth();
939 int originalHeight = original.getHeight();
940 int newWidth;
941 int newHeight;
942
943 if (originalWidth > originalHeight) {
944 newHeight = (int) (originalWidth / targetAspectRatio);
945 newWidth = originalWidth;
946 } else {
947 newWidth = (int) (originalHeight / targetAspectRatio);
948 newHeight = originalHeight;
949 }
950 int xOffset = (originalWidth - newWidth)/2;
951 int yOffset = (originalHeight - newHeight)/2;
952
953 if (xOffset < 0 || yOffset < 0) {
954 return dataBundle;
955 }
956
957 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
958 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
959 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
960
961 ByteArrayOutputStream stream = new ByteArrayOutputStream();
962
963 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
964 dataBundle.jpegData = stream.toByteArray();
965 return dataBundle;
966 }
967
Angus Kong9ef99252013-07-18 18:04:19 -0700968 private final class JpegPictureCallback
969 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800970 Location mLocation;
971
972 public JpegPictureCallback(Location loc) {
973 mLocation = loc;
974 }
975
976 @Override
Angus Kong454d63f2014-05-06 14:45:59 -0700977 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700978 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800979 if (mPaused) {
980 return;
981 }
Doris Liu6432cd62013-06-13 17:20:31 -0700982 if (mIsImageCaptureIntent) {
983 stopPreview();
984 }
Angus Kong6607dae2014-06-10 16:07:45 -0700985 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700986 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800987 }
988
989 mJpegPictureCallbackTime = System.currentTimeMillis();
990 // If postview callback has arrived, the captured image is displayed
991 // in postview callback. If not, the captured image is displayed in
992 // raw picture callback.
993 if (mPostViewPictureCallbackTime != 0) {
994 mShutterToPictureDisplayedTime =
995 mPostViewPictureCallbackTime - mShutterCallbackTime;
996 mPictureDisplayedToJpegCallbackTime =
997 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
998 } else {
999 mShutterToPictureDisplayedTime =
1000 mRawPictureCallbackTime - mShutterCallbackTime;
1001 mPictureDisplayedToJpegCallbackTime =
1002 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1003 }
1004 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1005 + mPictureDisplayedToJpegCallbackTime + "ms");
1006
Michael Kolb8872c232013-01-29 10:33:22 -08001007 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1008 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001009 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001010 }
1011
Angus Kong454d63f2014-05-06 14:45:59 -07001012 long now = System.currentTimeMillis();
1013 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1014 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1015 mJpegPictureCallbackTime = 0;
1016
1017 final ExifInterface exif = Exif.getExif(originalJpegData);
1018
1019 if (mShouldResizeTo16x9) {
1020 final ResizeBundle dataBundle = new ResizeBundle();
1021 dataBundle.jpegData = originalJpegData;
1022 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1023 dataBundle.exif = exif;
1024 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1025
1026 @Override
1027 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1028 return cropJpegDataToAspectRatio(resizeBundles[0]);
1029 }
1030
1031 @Override
1032 protected void onPostExecute(ResizeBundle result) {
1033 saveFinalPhoto(result.jpegData, result.exif, camera);
1034 }
1035 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1036
1037 } else {
1038 saveFinalPhoto(originalJpegData, exif, camera);
1039 }
1040 }
1041
1042 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1043
Doris Liu36e56fb2013-09-11 17:38:08 -07001044 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001045
Andy Huiberse08bc042014-04-11 19:26:47 -07001046 float zoomValue = 0f;
Angus Kong88289042014-04-22 16:39:42 -07001047 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Angus Kong6607dae2014-06-10 16:07:45 -07001048 int zoomIndex = mCameraSettings.getCurrentZoomIndex();
1049 List<Integer> zoomRatios = mCameraCapabilities.getZoomRatioList();
Andy Huiberse08bc042014-04-11 19:26:47 -07001050 if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
1051 zoomValue = 0.01f * zoomRatios.get(zoomIndex);
1052 }
1053 }
Angus Kong454d63f2014-05-06 14:45:59 -07001054
Angus Kong6607dae2014-06-10 16:07:45 -07001055 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
Andy Huibers203abe52014-05-19 13:59:01 -07001056 String flashSetting =
1057 mActivity.getSettingsManager().get(SettingsManager.SETTING_FLASH_MODE);
1058 boolean gridLinesOn = mActivity.getSettingsManager().areGridLinesOn();
Andy Huibers10c58162014-03-29 14:06:54 -07001059 UsageStatistics.instance().photoCaptureDoneEvent(
1060 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1061 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers547d7c82014-05-20 23:03:18 -07001062 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
1063 (float) mTimerDuration);
Ruben Brunkd217ed02013-10-08 23:31:13 -07001064
Ruben Brunkd7488272013-10-10 18:45:53 -07001065 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001066 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001067 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1068 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001069 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001070 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1071 width = exifWidth;
1072 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001073 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001074 Size s;
Angus Kong6607dae2014-06-10 16:07:45 -07001075 s = mCameraSettings.getCurrentPhotoSize();
Angus Kong454d63f2014-05-06 14:45:59 -07001076 if ((mJpegRotation + orientation) % 180 == 0) {
1077 width = s.width();
1078 height = s.height();
1079 } else {
1080 width = s.height();
1081 height = s.width();
1082 }
Michael Kolb8872c232013-01-29 10:33:22 -08001083 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001084 NamedEntity name = mNamedImages.getNextNameEntity();
1085 String title = (name == null) ? null : name.title;
1086 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001087
1088 // Handle debug mode outputs
1089 if (mDebugUri != null) {
1090 // If using a debug uri, save jpeg there.
1091 saveToDebugUri(jpegData);
1092
1093 // Adjust the title of the debug image shown in mediastore.
1094 if (title != null) {
1095 title = DEBUG_IMAGE_PREFIX + title;
1096 }
1097 }
1098
Michael Kolb8872c232013-01-29 10:33:22 -08001099 if (title == null) {
1100 Log.e(TAG, "Unbalanced name/data pair");
1101 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001102 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001103 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001104 }
Angus Kong0d00a892013-03-26 11:40:40 -07001105 if (mHeading >= 0) {
1106 // heading direction has been updated by the sensor.
1107 ExifTag directionRefTag = exif.buildTag(
1108 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1109 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1110 ExifTag directionTag = exif.buildTag(
1111 ExifInterface.TAG_GPS_IMG_DIRECTION,
1112 new Rational(mHeading, 1));
1113 exif.setTag(directionRefTag);
1114 exif.setTag(directionTag);
1115 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001116 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001117 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001118 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001119 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001120 // Animate capture with real jpeg data instead of a preview
1121 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001122 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001123 } else {
1124 mJpegImageData = jpegData;
1125 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001126 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001127 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001128 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001129 }
1130 }
1131
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001132 // Send the taken photo to remote shutter listeners, if any are
1133 // registered.
1134 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
1135 @Override
1136 public void run() {
1137 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1138 }
1139 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001140
Michael Kolb8872c232013-01-29 10:33:22 -08001141 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001142 // latency. It's true that someone else could write to the SD card
1143 // in the mean time and fill it, but that could have happened
1144 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001145 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001146 }
1147 }
1148
Angus Kong9ef99252013-07-18 18:04:19 -07001149 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001150 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001151 public void onAutoFocus(boolean focused, CameraProxy camera) {
1152 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001153 if (mPaused) {
1154 return;
1155 }
Michael Kolb8872c232013-01-29 10:33:22 -08001156
1157 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001158 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001159 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001160 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001161 }
1162 }
1163
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001164 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001165 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001166 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001167 @Override
1168 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001169 boolean moving, CameraProxy camera) {
1170 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001171 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001172 }
1173 }
1174
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001175 /**
1176 * This class is just a thread-safe queue for name,date holder objects.
1177 */
1178 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001179 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001180
1181 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001182 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001183 }
1184
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001185 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001186 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001187 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001188 r.date = date;
1189 mQueue.add(r);
1190 }
1191
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001192 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001193 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001194 if (!mQueue.isEmpty()) {
1195 return mQueue.remove(0);
1196 }
Michael Kolb8872c232013-01-29 10:33:22 -08001197 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001198 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001199 }
1200
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001201 public static class NamedEntity {
1202 public String title;
1203 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001204 }
1205 }
1206
1207 private void setCameraState(int state) {
1208 mCameraState = state;
1209 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001210 case PREVIEW_STOPPED:
1211 case SNAPSHOT_IN_PROGRESS:
1212 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001213 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001214 break;
1215 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001216 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001217 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001218 }
1219 }
1220
Sascha Haeberling37f36112013-08-06 14:31:52 -07001221 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001222 // Only animate when in full screen capture mode
1223 // i.e. If monkey/a user swipes to the gallery during picture taking,
1224 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001225 if (!mIsImageCaptureIntent) {
1226 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001227 }
Michael Kolb8872c232013-01-29 10:33:22 -08001228 }
1229
1230 @Override
1231 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001232 // If we are already in the middle of taking a snapshot or the image
1233 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001234 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001235 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001236 return false;
1237 }
1238 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001239
Michael Kolb8872c232013-01-29 10:33:22 -08001240 mPostViewPictureCallbackTime = 0;
1241 mJpegImageData = null;
1242
Angus Kong6607dae2014-06-10 16:07:45 -07001243 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001244
1245 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001246 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001247 }
1248
1249 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -08001250 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001251
Doris Liu3cf565c2013-02-15 10:55:37 -08001252 // We need to be consistent with the framework orientation (i.e. the
1253 // orientation of the UI.) when the auto-rotate screen setting is on.
1254 if (mActivity.isAutoRotateScreen()) {
1255 orientation = (360 - mDisplayRotation) % 360;
1256 } else {
1257 orientation = mOrientation;
1258 }
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +01001259 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
1260 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
Angus Kong6607dae2014-06-10 16:07:45 -07001261 mCameraSettings.setPhotoRotationDegrees(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001262 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kong6607dae2014-06-10 16:07:45 -07001263 CameraUtil.setGpsParameters(mCameraSettings, loc);
1264 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb8872c232013-01-29 10:33:22 -08001265
Sascha Haeberling88901942013-08-28 17:49:00 -07001266 // We don't want user to press the button again while taking a
1267 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001268 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001269 mCameraDevice.takePicture(mHandler,
1270 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001271 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001272 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001273
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001274 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001275
1276 mFaceDetectionStarted = false;
1277 setCameraState(SNAPSHOT_IN_PROGRESS);
1278 return true;
1279 }
1280
1281 @Override
1282 public void setFocusParameters() {
1283 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1284 }
1285
Michael Kolbd6954f32013-03-08 20:43:01 -08001286 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001287 // If scene mode is set, we cannot set flash mode, white balance, and
1288 // focus mode, instead, we read it from driver
Angus Kong6607dae2014-06-10 16:07:45 -07001289 if (CameraCapabilities.SceneMode.AUTO != mSceneMode) {
1290 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
1291 mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001292 }
1293 }
1294
Angus Kong6607dae2014-06-10 16:07:45 -07001295 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
1296 CameraCapabilities.FocusMode focusMode) {
1297 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrene419b192013-12-03 13:10:27 -08001298 SettingsManager settingsManager = mActivity.getSettingsManager();
Angus Kong6607dae2014-06-10 16:07:45 -07001299 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, stringifier.stringify(flashMode));
1300 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, stringifier.stringify(focusMode));
Michael Kolb8872c232013-01-29 10:33:22 -08001301 }
1302
1303 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001304 public void onOrientationChanged(int orientation) {
1305 // We keep the last known orientation. So if the user first orient
1306 // the camera then point the camera to floor or sky, we still have
1307 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001308 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1309 return;
1310 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001311 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001312 }
1313
1314 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001315 public void onCameraAvailable(CameraProxy cameraProxy) {
1316 if (mPaused) {
1317 return;
1318 }
1319 mCameraDevice = cameraProxy;
1320
1321 initializeCapabilities();
1322
1323 // Reset zoom value index.
1324 mZoomValue = 0;
1325 if (mFocusManager == null) {
1326 initializeFocusManager();
1327 }
Angus Kong88289042014-04-22 16:39:42 -07001328 mFocusManager.setParameters(mInitialParams, mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001329
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001330 // Do camera parameter dependent initialization.
Angus Kong6607dae2014-06-10 16:07:45 -07001331 mCameraSettings = mCameraDevice.getSettings();
Angus Kong20fad242013-11-11 18:23:46 -08001332 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001333 // Set a listener which updates camera parameters based
1334 // on changed settings.
1335 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001336 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001337 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001338
Angus Kong20fad242013-11-11 18:23:46 -08001339 startPreview();
1340
1341 onCameraOpened();
1342 }
1343
1344 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001345 public void onCaptureCancelled() {
1346 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1347 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001348 }
1349
Michael Kolbd6954f32013-03-08 20:43:01 -08001350 @Override
1351 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001352 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001353 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001354 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001355 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001356 setupPreview();
1357 }
1358
Michael Kolbd6954f32013-03-08 20:43:01 -08001359 @Override
1360 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001361 if (mPaused) {
1362 return;
1363 }
1364
1365 byte[] data = mJpegImageData;
1366
1367 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001368 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001369 // caller specifies a "save uri" then write the data to its
1370 // stream. Otherwise, pass back a scaled down version of the bitmap
1371 // directly in the extras.
1372 if (mSaveUri != null) {
1373 OutputStream outputStream = null;
1374 try {
1375 outputStream = mContentResolver.openOutputStream(mSaveUri);
1376 outputStream.write(data);
1377 outputStream.close();
1378
1379 mActivity.setResultEx(Activity.RESULT_OK);
1380 mActivity.finish();
1381 } catch (IOException ex) {
1382 // ignore exception
1383 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001384 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001385 }
1386 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001387 ExifInterface exif = Exif.getExif(data);
1388 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001389 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1390 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001391 mActivity.setResultEx(Activity.RESULT_OK,
1392 new Intent("inline-data").putExtra("data", bitmap));
1393 mActivity.finish();
1394 }
1395 } else {
1396 // Save the image to a temp file and invoke the cropper
1397 Uri tempUri = null;
1398 FileOutputStream tempStream = null;
1399 try {
1400 File path = mActivity.getFileStreamPath(sTempCropFilename);
1401 path.delete();
1402 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1403 tempStream.write(data);
1404 tempStream.close();
1405 tempUri = Uri.fromFile(path);
1406 } catch (FileNotFoundException ex) {
1407 mActivity.setResultEx(Activity.RESULT_CANCELED);
1408 mActivity.finish();
1409 return;
1410 } catch (IOException ex) {
1411 mActivity.setResultEx(Activity.RESULT_CANCELED);
1412 mActivity.finish();
1413 return;
1414 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001415 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001416 }
1417
1418 Bundle newExtras = new Bundle();
1419 if (mCropValue.equals("circle")) {
1420 newExtras.putString("circleCrop", "true");
1421 }
1422 if (mSaveUri != null) {
1423 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1424 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001425 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001426 }
1427 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001428 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001429 }
1430
Sascha Haeberling37f36112013-08-06 14:31:52 -07001431 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001432 final String CROP_ACTION = "com.android.camera.action.CROP";
1433 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001434
1435 cropIntent.setData(tempUri);
1436 cropIntent.putExtras(newExtras);
1437
1438 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1439 }
1440 }
1441
Michael Kolb8872c232013-01-29 10:33:22 -08001442 @Override
1443 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001444 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001445 }
1446
1447 @Override
1448 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001449 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001450 || (mCameraState == PREVIEW_STOPPED)) {
1451 return;
1452 }
Michael Kolb8872c232013-01-29 10:33:22 -08001453
1454 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001455 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001456 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001457 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001458 return;
1459 }
Alan Newbergerd41766f2014-04-09 18:25:34 -07001460 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
Michael Kolb8872c232013-01-29 10:33:22 -08001461
Doris Liu6c751642014-05-05 18:43:26 -07001462 int countDownDuration = Integer.parseInt(mActivity.getSettingsManager()
1463 .get(SettingsManager.SETTING_COUNTDOWN_DURATION));
Andy Huibers547d7c82014-05-20 23:03:18 -07001464 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001465 if (countDownDuration > 0) {
1466 // Start count down.
1467 mAppController.getCameraAppUI().transitionToCancel();
1468 mAppController.getCameraAppUI().hideModeOptions();
1469 mUI.startCountdown(countDownDuration);
1470 return;
1471 } else {
1472 focusAndCapture();
1473 }
1474 }
1475
1476 private void focusAndCapture() {
Angus Kong6607dae2014-06-10 16:07:45 -07001477 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001478 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001479 }
Michael Kolb8872c232013-01-29 10:33:22 -08001480 // If the user wants to do a snapshot while the previous one is still
1481 // in progress, remember the fact and do it after we finish the previous
1482 // one and re-start the preview. Snapshot in progress also includes the
1483 // state that autofocus is focusing and a picture will be taken when
1484 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001485 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1486 if (!mIsImageCaptureIntent) {
1487 mSnapshotOnIdle = true;
1488 }
Michael Kolb8872c232013-01-29 10:33:22 -08001489 return;
1490 }
1491
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001492 mSnapshotOnIdle = false;
Angus Kongeaaf56d2014-02-20 11:59:10 -08001493 mFocusManager.focusAndCapture();
Michael Kolb8872c232013-01-29 10:33:22 -08001494 }
1495
Doris Liu6c751642014-05-05 18:43:26 -07001496 @Override
1497 public void onRemainingSecondsChanged(int remainingSeconds) {
1498 mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
1499 }
1500
1501 @Override
1502 public void onCountDownFinished() {
1503 mAppController.getCameraAppUI().transitionToCapture();
1504 mAppController.getCameraAppUI().showModeOptions();
1505 if (mPaused) {
1506 return;
1507 }
1508 focusAndCapture();
1509 }
1510
Andy Huibersdef975d2013-11-22 09:13:39 -08001511 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001512 if (mPaused) {
1513 return;
1514 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001515 Log.v(TAG, "Executing onResumeTasks.");
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001516 CameraProvider camProvider = mActivity.getCameraProvider();
1517 if (camProvider == null) {
1518 // No camera provider, the Activity is destroyed already.
1519 return;
1520 }
1521 camProvider.requestCamera(mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -08001522
Michael Kolb8872c232013-01-29 10:33:22 -08001523 mJpegPictureCallbackTime = 0;
1524 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001525
1526 mOnResumeTime = SystemClock.uptimeMillis();
1527 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001528
1529 // If first time initialization is not finished, put it in the
1530 // message queue.
1531 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001532 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001533 } else {
1534 initializeSecondTime();
1535 }
Michael Kolb8872c232013-01-29 10:33:22 -08001536
Angus Kong0d00a892013-03-26 11:40:40 -07001537 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1538 if (gsensor != null) {
1539 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1540 }
1541
1542 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1543 if (msensor != null) {
1544 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1545 }
Michael Kolb8872c232013-01-29 10:33:22 -08001546 }
1547
Angus Kongc4e66562013-11-22 23:03:21 -08001548 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001549 * @return Whether the currently active camera is front-facing.
1550 */
1551 private boolean isCameraFrontFacing() {
1552 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1553 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1554 }
1555
1556 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001557 * The focus manager is the first UI related element to get initialized, and
1558 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001559 */
1560 private void initializeFocusManager() {
1561 // Create FocusManager object. startPreview needs it.
1562 // if mFocusManager not null, reuse it
1563 // otherwise create a new instance
1564 if (mFocusManager != null) {
1565 mFocusManager.removeMessages();
1566 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001567 mMirror = isCameraFrontFacing();
Angus Kongc4e66562013-11-22 23:03:21 -08001568 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1569 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001570 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1571 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001572 mInitialParams, this, mMirror,
Doris Liu482de022013-12-18 19:18:16 -08001573 mActivity.getMainLooper(), mUI.getFocusUI());
Angus Kongc4e66562013-11-22 23:03:21 -08001574 }
Doris Liu482de022013-12-18 19:18:16 -08001575 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001576 }
1577
1578 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001579 public void resume() {
1580 mPaused = false;
Doris Liu6c751642014-05-05 18:43:26 -07001581 mCountdownSoundPlayer.loadSounds();
Doris Liu482de022013-12-18 19:18:16 -08001582 if (mFocusManager != null) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001583 // If camera is not open when resume is called, focus manager will
1584 // not
Doris Liu15b99612013-12-21 11:32:28 -08001585 // be initialized yet, in which case it will start listening to
1586 // preview area size change later in the initialization.
Doris Liu482de022013-12-18 19:18:16 -08001587 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1588 }
Doris Liu6c751642014-05-05 18:43:26 -07001589 mAppController.addPreviewAreaSizeChangedListener(mUI);
Doris Liu59401042014-01-14 17:51:32 -08001590
Angus Kongc4e66562013-11-22 23:03:21 -08001591 // Add delay on resume from lock screen only, in order to to speed up
1592 // the onResume --> onPause --> onResume cycle from lock screen.
1593 // Don't do always because letting go of thread can cause delay.
1594 String action = mActivity.getIntent().getAction();
1595 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1596 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1597 Log.v(TAG, "On resume, from lock screen.");
1598 // Note: onPauseAfterSuper() will delete this runnable, so we will
1599 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001600 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001601 } else {
1602 Log.v(TAG, "On resume.");
1603 onResumeTasks();
1604 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001605 getServices().getRemoteShutterListener().onModuleReady(this);
Andy Huibers10c58162014-03-29 14:06:54 -07001606 SessionStatsCollector.instance().sessionActive(true);
Angus Kongc4e66562013-11-22 23:03:21 -08001607 }
1608
1609 @Override
1610 public void pause() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001611 getServices().getRemoteShutterListener().onModuleExit();
Michael Kolb8872c232013-01-29 10:33:22 -08001612 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001613 mHandler.removeCallbacks(mResumeTaskRunnable);
Andy Huibers10c58162014-03-29 14:06:54 -07001614 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001615
Angus Kong0d00a892013-03-26 11:40:40 -07001616 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1617 if (gsensor != null) {
1618 mSensorManager.unregisterListener(this, gsensor);
1619 }
1620
1621 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1622 if (msensor != null) {
1623 mSensorManager.unregisterListener(this, msensor);
1624 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001625
Michael Kolb8872c232013-01-29 10:33:22 -08001626 // Reset the focus first. Camera CTS does not guarantee that
1627 // cancelAutoFocus is allowed after preview stops.
1628 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1629 mCameraDevice.cancelAutoFocus();
1630 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001631
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001632 // If the camera has not been opened asynchronously yet,
1633 // and startPreview hasn't been called, then this is a no-op.
1634 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001635 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001636 cancelCountDown();
1637 mCountdownSoundPlayer.release();
Michael Kolb8872c232013-01-29 10:33:22 -08001638
Angus Kongce5480e2013-01-29 17:43:48 -08001639 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001640 // If we are in an image capture intent and has taken
1641 // a picture, we just clear it in onPause.
1642 mJpegImageData = null;
1643
Angus Kongdcccc512013-08-08 17:06:03 -07001644 // Remove the messages and runnables in the queue.
1645 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001646
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001647 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001648 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001649 mUI.onPause();
1650
Michael Kolb8872c232013-01-29 10:33:22 -08001651 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001652 if (mFocusManager != null) {
1653 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001654 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001655 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001656 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001657 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001658
1659 SettingsManager settingsManager = mActivity.getSettingsManager();
1660 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001661 }
1662
Angus Kong20fad242013-11-11 18:23:46 -08001663 @Override
1664 public void destroy() {
1665 // TODO: implement this.
1666 }
1667
1668 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001669 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001670 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001671 }
1672
1673 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001674 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001675 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001676 setDisplayOrientation();
1677 }
1678 }
1679
Michael Kolb8872c232013-01-29 10:33:22 -08001680 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001681 return isCameraIdle()
1682 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001683 }
1684
1685 @Override
1686 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001687 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001688 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001689 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001690 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001691 setCameraState(FOCUSING);
1692 }
1693
1694 @Override
1695 public void cancelAutoFocus() {
1696 mCameraDevice.cancelAutoFocus();
1697 setCameraState(IDLE);
1698 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1699 }
1700
Michael Kolb8872c232013-01-29 10:33:22 -08001701 @Override
1702 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001703 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1704 || mCameraState == SNAPSHOT_IN_PROGRESS
1705 || mCameraState == SWITCHING_CAMERA
1706 || mCameraState == PREVIEW_STOPPED) {
1707 return;
1708 }
1709
1710 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001711 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1712 return;
1713 }
Doris Liu482de022013-12-18 19:18:16 -08001714 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001715 }
1716
1717 @Override
1718 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001719 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001720 }
1721
1722 @Override
1723 public boolean onKeyDown(int keyCode, KeyEvent event) {
1724 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001725 case KeyEvent.KEYCODE_VOLUME_UP:
1726 case KeyEvent.KEYCODE_VOLUME_DOWN:
1727 case KeyEvent.KEYCODE_FOCUS:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001728 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001729 if (event.getRepeatCount() == 0) {
1730 onShutterButtonFocus(true);
1731 }
1732 return true;
1733 }
1734 return false;
1735 case KeyEvent.KEYCODE_CAMERA:
1736 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1737 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001738 }
1739 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001740 case KeyEvent.KEYCODE_DPAD_CENTER:
1741 // If we get a dpad center event without any focused view, move
1742 // the focus to the shutter button and press it.
1743 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1744 // Start auto-focus immediately to reduce shutter lag. After
1745 // the shutter button gets the focus, onShutterButtonFocus()
1746 // will be called again but it is fine.
1747 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001748 }
1749 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001750 }
1751 return false;
1752 }
1753
1754 @Override
1755 public boolean onKeyUp(int keyCode, KeyEvent event) {
1756 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001757 case KeyEvent.KEYCODE_VOLUME_UP:
1758 case KeyEvent.KEYCODE_VOLUME_DOWN:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001759 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001760 onShutterButtonClick();
1761 return true;
1762 }
1763 return false;
1764 case KeyEvent.KEYCODE_FOCUS:
1765 if (mFirstTimeInitialized) {
1766 onShutterButtonFocus(false);
1767 }
Michael Kolb8872c232013-01-29 10:33:22 -08001768 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001769 }
1770 return false;
1771 }
1772
Michael Kolb8872c232013-01-29 10:33:22 -08001773 private void closeCamera() {
1774 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001775 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001776 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001777 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001778 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001779
Michael Kolb8872c232013-01-29 10:33:22 -08001780 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001781 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001782 mCameraDevice = null;
1783 setCameraState(PREVIEW_STOPPED);
1784 mFocusManager.onCameraReleased();
1785 }
1786 }
1787
1788 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001789 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1790 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001791 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001792 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001793 if (mFocusManager != null) {
1794 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1795 }
Doris Liu6432cd62013-06-13 17:20:31 -07001796 // Change the camera display orientation
1797 if (mCameraDevice != null) {
1798 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1799 }
Michael Kolb8872c232013-01-29 10:33:22 -08001800 }
1801
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001802 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001803 private void setupPreview() {
1804 mFocusManager.resetTouchFocus();
1805 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001806 }
1807
Angus Kong20fad242013-11-11 18:23:46 -08001808 /**
1809 * Returns whether we can/should start the preview or not.
1810 */
1811 private boolean checkPreviewPreconditions() {
1812 if (mPaused) {
1813 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001814 }
Michael Kolb8872c232013-01-29 10:33:22 -08001815
Angus Kong20fad242013-11-11 18:23:46 -08001816 if (mCameraDevice == null) {
1817 Log.w(TAG, "startPreview: camera device not ready yet.");
1818 return false;
1819 }
1820
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001821 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001822 if (st == null) {
1823 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001824 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001825 }
1826
1827 if (!mCameraPreviewParamsReady) {
1828 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001829 return false;
1830 }
1831 return true;
1832 }
1833
1834 /**
1835 * The start/stop preview should only run on the UI thread.
1836 */
1837 private void startPreview() {
1838 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001839 return;
1840 }
Angus Kong20fad242013-11-11 18:23:46 -08001841
Angus Kong2bca2102014-03-11 16:27:30 -07001842 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001843 setDisplayOrientation();
1844
1845 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001846 // If the focus mode is continuous autofocus, call cancelAutoFocus
1847 // to resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001848 String focusMode = mFocusManager.getFocusMode();
1849 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001850 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001851 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001852 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1853 }
1854 setCameraParameters(UPDATE_PARAM_ALL);
Doris Liucb8215e2014-05-19 11:53:32 -07001855
1856 // Workaround for KitKat and KitKat MR1 which leave configured preview
1857 // callback streams lingering around when they should have been removed.
1858 // These preview callback streams are the cause for distorted preview.
1859 // For more details, see b/12210027
Doris Liu6a8ae812014-05-20 12:43:56 -07001860 if (mShouldSetPreviewCallbacks) {
1861 mShouldSetPreviewCallbacks = false;
Doris Liucb8215e2014-05-19 11:53:32 -07001862 Size previewSize = new Size(mCameraDevice.getParameters().getPreviewSize());
1863 mCameraDevice.setPreviewDataCallbackWithBuffer(mHandler,
1864 new CameraManager.CameraPreviewDataCallback() {
1865 @Override
1866 public void onPreviewFrame(byte[] data, CameraProxy camera) {
1867 // Remove callback after the first frame comes in.
1868 mCameraDevice.setPreviewDataCallbackWithBuffer(null, null);
1869 }
1870 });
1871 mCameraDevice.addCallbackBuffer(new byte[previewSize.width() * previewSize.height()]);
1872 }
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001873 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001874
Alan Newbergerd41766f2014-04-09 18:25:34 -07001875 Log.i(TAG, "startPreview");
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001876 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001877
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001878 mFocusManager.onPreviewStarted();
1879 onPreviewStarted();
Andy Huibers10c58162014-03-29 14:06:54 -07001880 SessionStatsCollector.instance().previewActive(true);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001881 if (mSnapshotOnIdle) {
1882 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001883 }
1884 }
1885
Michael Kolbd6954f32013-03-08 20:43:01 -08001886 @Override
1887 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001888 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001889 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001890 mCameraDevice.stopPreview();
1891 mFaceDetectionStarted = false;
1892 }
1893 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001894 if (mFocusManager != null) {
1895 mFocusManager.onPreviewStopped();
1896 }
Andy Huibers10c58162014-03-29 14:06:54 -07001897 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001898 }
1899
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001900 @Override
Erin Dahlgren1648c362014-01-06 15:06:04 -08001901 public void onSettingChanged(SettingsManager settingsManager, int id) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001902 switch (id) {
1903 case SettingsManager.SETTING_FLASH_MODE: {
1904 updateParametersFlashMode();
1905 break;
1906 }
Angus Kong8c07fe92014-05-22 14:42:45 -07001907 case SettingsManager.SETTING_CAMERA_HDR: {
Angus Kong3699c412014-05-23 15:31:09 -07001908 String val = settingsManager.get(SettingsManager.SETTING_CAMERA_HDR);
Angus Kong8c07fe92014-05-22 14:42:45 -07001909 if (SettingsManager.VALUE_ON.equals(val)) {
Angus Kong3699c412014-05-23 15:31:09 -07001910 // HDR is on.
Angus Kong8c07fe92014-05-22 14:42:45 -07001911 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
Angus Kong3699c412014-05-23 15:31:09 -07001912 mFlashModeBeforeSceneMode = settingsManager.get(SettingsManager
1913 .SETTING_FLASH_MODE);
Angus Kong8c07fe92014-05-22 14:42:45 -07001914 } else {
Angus Kong3699c412014-05-23 15:31:09 -07001915 if (mFlashModeBeforeSceneMode != null) {
1916 settingsManager.set(SettingsManager.SETTING_FLASH_MODE,
1917 mFlashModeBeforeSceneMode);
1918 updateParametersFlashMode();
1919 mFlashModeBeforeSceneMode = null;
1920 }
Angus Kong8c07fe92014-05-22 14:42:45 -07001921 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
1922 }
1923 break;
1924 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001925 default: {
1926 // Do nothing.
1927 }
1928 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001929
1930 if (mCameraDevice != null) {
Angus Kong6607dae2014-06-10 16:07:45 -07001931 mCameraDevice.applySettings(mCameraSettings);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001932 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001933 }
1934
Michael Kolb8872c232013-01-29 10:33:22 -08001935 private void updateCameraParametersInitialize() {
1936 // Reset preview frame rate to the maximum because it may be lowered by
1937 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07001938 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07001939 if (fpsRange != null && fpsRange.length > 0) {
Angus Kong6607dae2014-06-10 16:07:45 -07001940 mCameraSettings.setPreviewFpsRange(
Doris Liu6432cd62013-06-13 17:20:31 -07001941 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1942 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001943 }
1944
Angus Kong6607dae2014-06-10 16:07:45 -07001945 mCameraSettings.setSetting(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001946
Angus Kong6607dae2014-06-10 16:07:45 -07001947 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
1948 mCameraSettings.setVideoStabilization(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001949 }
1950 }
1951
1952 private void updateCameraParametersZoom() {
1953 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07001954 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Angus Kong6607dae2014-06-10 16:07:45 -07001955 mCameraSettings.setZoomIndex(mZoomValue);
Michael Kolb8872c232013-01-29 10:33:22 -08001956 }
1957 }
1958
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001959 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001960 private void setAutoExposureLockIfSupported() {
1961 if (mAeLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07001962 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08001963 }
1964 }
1965
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001966 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001967 private void setAutoWhiteBalanceLockIfSupported() {
1968 if (mAwbLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07001969 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08001970 }
1971 }
1972
Michael Kolb8872c232013-01-29 10:33:22 -08001973 private void setFocusAreasIfSupported() {
1974 if (mFocusAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07001975 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08001976 }
1977 }
1978
Michael Kolb8872c232013-01-29 10:33:22 -08001979 private void setMeteringAreasIfSupported() {
1980 if (mMeteringAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07001981 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08001982 }
1983 }
1984
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001985 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001986 setAutoExposureLockIfSupported();
1987 setAutoWhiteBalanceLockIfSupported();
1988 setFocusAreasIfSupported();
1989 setMeteringAreasIfSupported();
1990
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001991 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07001992 mFocusManager.overrideFocusMode(null);
Angus Kong6607dae2014-06-10 16:07:45 -07001993 mCameraSettings.setFocusMode(mCameraCapabilities.getStringifier()
1994 .focusModeFromString(mFocusManager.getFocusMode()));
Andy Huibers10c58162014-03-29 14:06:54 -07001995 SessionStatsCollector.instance().autofocusActive(
1996 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolbd3253f22013-07-12 11:36:47 -07001997
Michael Kolb8872c232013-01-29 10:33:22 -08001998 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001999 updateParametersPictureSize();
2000
2001 // Set JPEG quality.
2002 updateParametersPictureQuality();
2003
2004 // For the following settings, we need to check if the settings are
2005 // still supported by latest driver, if not, ignore the settings.
2006
2007 // Set exposure compensation
2008 updateParametersExposureCompensation();
2009
2010 // Set the scene mode: also sets flash and white balance.
2011 updateParametersSceneMode();
2012
2013 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2014 updateAutoFocusMoveCallback();
2015 }
2016 }
2017
2018 private void updateParametersPictureSize() {
2019 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002020 String pictureSize = settingsManager
2021 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
2022 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002023
Angus Kong6607dae2014-06-10 16:07:45 -07002024 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Andy Huibers90c7ad52014-05-20 22:13:54 -07002025 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2026 mCameraDevice.getCameraId(), supported);
Angus Kong6607dae2014-06-10 16:07:45 -07002027 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002028 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002029
2030 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2031 mCameraDevice.getCameraId());
2032 if (ApiHelper.IS_NEXUS_5) {
2033 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2034 mShouldResizeTo16x9 = true;
2035 } else {
2036 mShouldResizeTo16x9 = false;
2037 }
2038 }
Michael Kolb8872c232013-01-29 10:33:22 -08002039
2040 // Set a preview size that is closest to the viewfinder height and has
2041 // the right aspect ratio.
Angus Kong6607dae2014-06-10 16:07:45 -07002042 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07002043 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002044 (double) size.width() / size.height());
Angus Kong6607dae2014-06-10 16:07:45 -07002045 Size original = mCameraSettings.getCurrentPreviewSize();
2046 if (!optimalSize.equals(original)) {
Doris Liu6a8ae812014-05-20 12:43:56 -07002047 if (ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK) {
2048 // Compare the aspect ratio.
2049 if ((original.width() * optimalSize.height())
2050 != (original.height() * optimalSize.width())) {
2051 // If aspect ratio has changed, set preview callback again, so
2052 // that the old preview callback stream will be forced to update.
2053 // This is a workaround for b/12210027, which was fixed in Kitkat MR2.
2054 mShouldSetPreviewCallbacks = true;
2055 }
2056 }
Angus Kong6607dae2014-06-10 16:07:45 -07002057 mCameraSettings.setPreviewSize(optimalSize);
Doris Liu6432cd62013-06-13 17:20:31 -07002058
Michael Kolb8872c232013-01-29 10:33:22 -08002059 // Zoom related settings will be changed for different preview
2060 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002061 if (mHandler.getLooper() == Looper.myLooper()) {
2062 // On UI thread only, not when camera starts up
2063 setupPreview();
2064 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002065 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002066 }
Angus Kong6607dae2014-06-10 16:07:45 -07002067 mCameraSettings = mCameraDevice.getSettings();
Michael Kolb8872c232013-01-29 10:33:22 -08002068 }
Doris Liu95405742013-11-05 15:25:26 -08002069
Angus Kong00b7b102014-04-24 15:46:52 -07002070 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2071 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2072 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002073 }
Angus Kong00b7b102014-04-24 15:46:52 -07002074 Log.i(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002075 }
Michael Kolb8872c232013-01-29 10:33:22 -08002076
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002077 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002078 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2079 CameraProfile.QUALITY_HIGH);
Angus Kong6607dae2014-06-10 16:07:45 -07002080 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002081 }
Michael Kolb8872c232013-01-29 10:33:22 -08002082
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002083 private void updateParametersExposureCompensation() {
2084 SettingsManager settingsManager = mActivity.getSettingsManager();
Spike Spragueabf54e22014-03-27 15:41:28 -07002085 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
2086 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
Angus Kong88289042014-04-22 16:39:42 -07002087 int max = mCameraCapabilities.getMaxExposureCompensation();
2088 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002089 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002090 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002091 } else {
2092 Log.w(TAG, "invalid exposure range: " + value);
2093 }
Michael Kolb8872c232013-01-29 10:33:22 -08002094 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002095 // If exposure compensation is not enabled, reset the exposure compensation value.
2096 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002097 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002098
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002099 }
2100
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002101 private void updateParametersSceneMode() {
Angus Kong6607dae2014-06-10 16:07:45 -07002102 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002103 SettingsManager settingsManager = mActivity.getSettingsManager();
2104
Angus Kong6607dae2014-06-10 16:07:45 -07002105 mSceneMode = stringifier
2106 .sceneModeFromString(settingsManager.get(SettingsManager.SETTING_SCENE_MODE));
2107 if (mCameraCapabilities.supports(mSceneMode)) {
2108 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2109 mCameraSettings.setSceneMode(mSceneMode);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002110
2111 // Setting scene mode will change the settings of flash mode,
2112 // white balance, and focus mode. Here we read back the
2113 // parameters, so we can know those settings.
Angus Kong6607dae2014-06-10 16:07:45 -07002114 mCameraDevice.applySettings(mCameraSettings);
2115 mCameraSettings = mCameraDevice.getSettings();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002116 }
2117 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002118 mSceneMode = mCameraSettings.getCurrentSceneMode();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002119 if (mSceneMode == null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002120 mSceneMode = CameraCapabilities.SceneMode.AUTO;
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002121 }
2122 }
2123
Angus Kong6607dae2014-06-10 16:07:45 -07002124 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
Michael Kolb8872c232013-01-29 10:33:22 -08002125 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002126 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002127
Michael Kolb8872c232013-01-29 10:33:22 -08002128 // Set focus mode.
2129 mFocusManager.overrideFocusMode(null);
Angus Kong6607dae2014-06-10 16:07:45 -07002130 mCameraSettings.setFocusMode(mCameraCapabilities.getStringifier()
2131 .focusModeFromString(mFocusManager.getFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002132 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002133 mFocusManager.overrideFocusMode(mCameraCapabilities.getStringifier()
2134 .stringify(mCameraSettings.getCurrentFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002135 }
Michael Kolb8872c232013-01-29 10:33:22 -08002136 }
2137
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002138 private void updateParametersFlashMode() {
2139 SettingsManager settingsManager = mActivity.getSettingsManager();
2140
Angus Kong6607dae2014-06-10 16:07:45 -07002141 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
2142 .flashModeFromString(settingsManager.get(SettingsManager.SETTING_FLASH_MODE));
2143 if (mCameraCapabilities.supports(flashMode)) {
2144 mCameraSettings.setFlashMode(flashMode);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002145 }
2146 }
2147
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002148 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002149 private void updateAutoFocusMoveCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -07002150 if (mCameraSettings.getCurrentFocusMode() ==
2151 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Angus Kong9ef99252013-07-18 18:04:19 -07002152 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002153 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002154 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002155 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002156 }
2157 }
2158
Spike Spragueabf54e22014-03-27 15:41:28 -07002159 /**
2160 * Sets the exposure compensation to the given value and also updates settings.
2161 *
2162 * @param value exposure compensation value to be set
2163 */
2164 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002165 int max = mCameraCapabilities.getMaxExposureCompensation();
2166 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002167 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002168 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002169 SettingsManager settingsManager = mActivity.getSettingsManager();
2170 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
2171 } else {
2172 Log.w(TAG, "invalid exposure range: " + value);
2173 }
2174 }
2175
Michael Kolb8872c232013-01-29 10:33:22 -08002176 // We separate the parameters into several subsets, so we can update only
2177 // the subsets actually need updating. The PREFERENCE set needs extra
2178 // locking because the preference can be changed from GLThread as well.
2179 private void setCameraParameters(int updateSet) {
2180 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2181 updateCameraParametersInitialize();
2182 }
2183
2184 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2185 updateCameraParametersZoom();
2186 }
2187
2188 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002189 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002190 }
2191
Angus Kong6607dae2014-06-10 16:07:45 -07002192 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb8872c232013-01-29 10:33:22 -08002193 }
2194
2195 // If the Camera is idle, update the parameters immediately, otherwise
2196 // accumulate them in mUpdateSet and update later.
2197 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2198 mUpdateSet |= additionalUpdateSet;
2199 if (mCameraDevice == null) {
2200 // We will update all the parameters when we open the device, so
2201 // we don't need to do anything now.
2202 mUpdateSet = 0;
2203 return;
2204 } else if (isCameraIdle()) {
2205 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002206 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002207 mUpdateSet = 0;
2208 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002209 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2210 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002211 }
2212 }
2213 }
2214
ztenghui7b265a62013-09-09 14:58:44 -07002215 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002216 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002217 return (mCameraState == IDLE) ||
2218 (mCameraState == PREVIEW_STOPPED) ||
2219 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002220 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002221 }
2222
ztenghui7b265a62013-09-09 14:58:44 -07002223 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002224 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002225 String action = mActivity.getIntent().getAction();
2226 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002227 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002228 }
2229
2230 private void setupCaptureParams() {
2231 Bundle myExtras = mActivity.getIntent().getExtras();
2232 if (myExtras != null) {
2233 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2234 mCropValue = myExtras.getString("crop");
2235 }
2236 }
2237
Michael Kolb8872c232013-01-29 10:33:22 -08002238 private void initializeCapabilities() {
2239 mInitialParams = mCameraDevice.getParameters();
Angus Kong88289042014-04-22 16:39:42 -07002240 mCameraCapabilities = mCameraDevice.getCapabilities();
2241 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2242 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2243 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2244 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kong6607dae2014-06-10 16:07:45 -07002245 mContinuousFocusSupported =
2246 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002247 }
2248
Marco Nelissen0744e4a2013-11-22 01:47:37 +00002249 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08002250 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002251 public int onZoomChanged(int index) {
2252 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002253 if (mPaused) {
2254 return index;
2255 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002256 mZoomValue = index;
Angus Kong6607dae2014-06-10 16:07:45 -07002257 if (mCameraSettings == null || mCameraDevice == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002258 return index;
2259 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002260 // Set zoom parameters asynchronously
Angus Kong6607dae2014-06-10 16:07:45 -07002261 mCameraSettings.setZoomRatio((float) mZoomValue);
2262 mCameraDevice.applySettings(mCameraSettings);
Michael Kolbd6954f32013-03-08 20:43:01 -08002263 Parameters p = mCameraDevice.getParameters();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002264 if (p != null) {
2265 return p.getZoom();
2266 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002267 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002268 }
2269
2270 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002271 public int getCameraState() {
2272 return mCameraState;
2273 }
2274
2275 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002276 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002277 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002278 }
Angus Kong86d36312013-01-31 18:22:44 -08002279
2280 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002281 public void onLowMemory() {
2282 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002283 }
Angus Kong0d00a892013-03-26 11:40:40 -07002284
2285 @Override
2286 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2287 }
2288
2289 @Override
2290 public void onSensorChanged(SensorEvent event) {
2291 int type = event.sensor.getType();
2292 float[] data;
2293 if (type == Sensor.TYPE_ACCELEROMETER) {
2294 data = mGData;
2295 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2296 data = mMData;
2297 } else {
2298 // we should not be here.
2299 return;
2300 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002301 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002302 data[i] = event.values[i];
2303 }
2304 float[] orientation = new float[3];
2305 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2306 SensorManager.getOrientation(mR, orientation);
2307 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2308 if (mHeading < 0) {
2309 mHeading += 360;
2310 }
Angus Kong0d00a892013-03-26 11:40:40 -07002311 }
Doris Liu6432cd62013-06-13 17:20:31 -07002312
Ruben Brunkd217ed02013-10-08 23:31:13 -07002313 // For debugging only.
2314 public void setDebugUri(Uri uri) {
2315 mDebugUri = uri;
2316 }
2317
2318 // For debugging only.
2319 private void saveToDebugUri(byte[] data) {
2320 if (mDebugUri != null) {
2321 OutputStream outputStream = null;
2322 try {
2323 outputStream = mContentResolver.openOutputStream(mDebugUri);
2324 outputStream.write(data);
2325 outputStream.close();
2326 } catch (IOException e) {
2327 Log.e(TAG, "Exception while writing debug jpeg file", e);
2328 } finally {
2329 CameraUtil.closeSilently(outputStream);
2330 }
2331 }
2332 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002333
2334 @Override
2335 public void onRemoteShutterPress() {
2336 capture();
2337 }
Doris Liu6c751642014-05-05 18:43:26 -07002338
2339 /**
2340 * This class manages the loading/releasing/playing of the sounds needed for
2341 * countdown timer.
2342 */
2343 private class CountdownSoundPlayer {
2344 private SoundPool mSoundPool;
2345 private int mBeepOnce;
2346 private int mBeepTwice;
2347
2348 void loadSounds() {
2349 // Load the beeps.
Seth Raphael20e20c92014-05-27 09:45:26 -07002350 if (mSoundPool == null) {
2351 mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2352 mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
2353 mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
2354 }
Doris Liu6c751642014-05-05 18:43:26 -07002355 }
2356
2357 void onRemainingSecondsChanged(int newVal) {
2358 if (mSoundPool == null) {
2359 Log.e(TAG, "Cannot play sound - they have not been loaded.");
2360 return;
2361 }
2362 if (newVal == 1) {
2363 mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
2364 } else if (newVal == 2 || newVal == 3) {
2365 mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
2366 }
2367 }
2368
2369 void release() {
Seth Raphael20e20c92014-05-27 09:45:26 -07002370 if (mSoundPool != null) {
2371 mSoundPool.release();
2372 mSoundPool = null;
2373 }
Doris Liu6c751642014-05-05 18:43:26 -07002374 }
2375 }
Michael Kolb8872c232013-01-29 10:33:22 -08002376}