blob: 3360122c58e4f5e8d907887b6a21cd3a32e38eae [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
Michael Kolb8872c232013-01-29 10:33:22 -080021import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070022import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080023import android.content.Intent;
Michael Kolb8872c232013-01-29 10:33:22 -080024import android.graphics.Bitmap;
Angus Kong454d63f2014-05-06 14:45:59 -070025import android.graphics.BitmapFactory;
Michael Kolb8872c232013-01-29 10:33:22 -080026import android.graphics.SurfaceTexture;
Angus Kong0d00a892013-03-26 11:40:40 -070027import android.hardware.Sensor;
28import android.hardware.SensorEvent;
29import android.hardware.SensorEventListener;
30import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080031import android.location.Location;
Spike Spragueeeeed4f2014-08-27 12:01:36 -070032import android.media.AudioManager;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.media.CameraProfile;
Spike Spragueeeeed4f2014-08-27 12:01:36 -070034import android.media.SoundPool;
Michael Kolb8872c232013-01-29 10:33:22 -080035import android.net.Uri;
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -070036import android.os.AsyncTask;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070037import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080038import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080039import android.os.Handler;
40import android.os.Looper;
41import android.os.Message;
42import android.os.MessageQueue;
43import android.os.SystemClock;
44import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080045import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080046import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080047import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080048
Sameer Padala2c8cc452013-11-05 18:49:12 -080049import com.android.camera.PhotoModule.NamedImages.NamedEntity;
50import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080051import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070052import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070053import com.android.camera.app.MediaSaver;
54import com.android.camera.app.MemoryManager;
55import com.android.camera.app.MemoryManager.MemoryListener;
Kevin Gabayanfb333362014-06-02 14:48:20 -070056import com.android.camera.app.MotionManager;
Angus Kong2bca2102014-03-11 16:27:30 -070057import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070058import com.android.camera.exif.ExifInterface;
59import com.android.camera.exif.ExifTag;
60import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080061import com.android.camera.hardware.HardwareSpec;
62import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080063import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080064import com.android.camera.remote.RemoteCameraModule;
Andy Huibers90c7ad52014-05-20 22:13:54 -070065import com.android.camera.settings.CameraPictureSizesCacher;
Erin Dahlgren6190c362014-06-13 14:12:08 -070066import com.android.camera.settings.Keys;
Angus Kong454d63f2014-05-06 14:45:59 -070067import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080068import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010069import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070070import com.android.camera.ui.CountDownView;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070071import com.android.camera.ui.TouchCoordinate;
Angus Kongdcccc512013-08-08 17:06:03 -070072import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070073import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070074import com.android.camera.util.GcamHelper;
Sol Boucher44ce4b22014-08-04 23:41:38 -070075import com.android.camera.util.GservicesHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070076import com.android.camera.util.SessionStatsCollector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070077import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070078import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070079import com.android.camera2.R;
Spike Sprague3c3b31d2014-09-08 10:43:04 -070080import com.android.ex.camera2.portability.CameraAgent;
Sol Boucher5a344962014-06-17 14:05:08 -070081import com.android.ex.camera2.portability.CameraAgent.CameraAFCallback;
82import com.android.ex.camera2.portability.CameraAgent.CameraAFMoveCallback;
83import com.android.ex.camera2.portability.CameraAgent.CameraPictureCallback;
84import com.android.ex.camera2.portability.CameraAgent.CameraProxy;
85import com.android.ex.camera2.portability.CameraAgent.CameraShutterCallback;
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -070086import com.android.ex.camera2.portability.CameraCapabilities;
Sol Boucher43e18132014-06-19 15:03:18 -070087import com.android.ex.camera2.portability.CameraDeviceInfo.Characteristics;
Sol Boucher5a344962014-06-17 14:05:08 -070088import com.android.ex.camera2.portability.CameraSettings;
89import com.android.ex.camera2.portability.Size;
Seth Raphael5e09d012013-12-18 13:45:03 -080090import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080091
Angus Kong454d63f2014-05-06 14:45:59 -070092import java.io.ByteArrayOutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070093import java.io.File;
94import java.io.FileNotFoundException;
95import java.io.FileOutputStream;
96import java.io.IOException;
97import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010098import java.lang.ref.WeakReference;
Angus Kong831347d2014-06-16 16:07:28 -070099import java.util.ArrayList;
Angus Kongdcccc512013-08-08 17:06:03 -0700100import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700101import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -0700102
Michael Kolb8872c232013-01-29 10:33:22 -0800103public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800104 extends CameraModule
105 implements PhotoController,
106 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800107 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800108 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -0800109 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800110 SettingsManager.OnSettingChangedListener,
Doris Liu6c751642014-05-05 18:43:26 -0700111 RemoteCameraModule,
112 CountDownView.OnCountDownStatusListener {
Michael Kolb8872c232013-01-29 10:33:22 -0800113
Alan Newberger00a390e2014-08-18 18:40:47 -0700114 public static final String PHOTO_MODULE_STRING_ID = "PhotoModule";
Erin Dahlgren6190c362014-06-13 14:12:08 -0700115
116 private static final Log.Tag TAG = new Log.Tag(PHOTO_MODULE_STRING_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800117
118 // We number the request code from 1000 to avoid collision with Gallery.
119 private static final int REQUEST_CROP = 1000;
120
Angus Kong13e87c42013-11-25 10:02:47 -0800121 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800122 private static final int MSG_FIRST_TIME_INIT = 1;
123 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800124
125 // The subset of parameters we need to update in setCameraParameters().
126 private static final int UPDATE_PARAM_INITIALIZE = 1;
127 private static final int UPDATE_PARAM_ZOOM = 2;
128 private static final int UPDATE_PARAM_PREFERENCE = 4;
129 private static final int UPDATE_PARAM_ALL = -1;
130
Andy Huibersdef975d2013-11-22 09:13:39 -0800131 // This is the delay before we execute onResume tasks when coming
132 // from the lock screen, to allow time for onPause to execute.
133 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800134
Ruben Brunkd7488272013-10-10 18:45:53 -0700135 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
136
Michael Kolb8872c232013-01-29 10:33:22 -0800137 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800138 private CameraProxy mCameraDevice;
139 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700140 private CameraCapabilities mCameraCapabilities;
Angus Kong6607dae2014-06-10 16:07:45 -0700141 private CameraSettings mCameraSettings;
Michael Kolb8872c232013-01-29 10:33:22 -0800142 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800143
144 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800145
Michael Kolb8872c232013-01-29 10:33:22 -0800146 // The activity is going to switch to the specified camera id. This is
147 // needed because texture copy is done in GL thread. -1 means camera is not
148 // switching.
149 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800150
151 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
152 // needed to be updated in mUpdateSet.
153 private int mUpdateSet;
154
Sol Boucher2192fba2014-08-19 17:24:07 -0700155 private float mZoomValue; // The current zoom ratio.
Andy Huibers547d7c82014-05-20 23:03:18 -0700156 private int mTimerDuration;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700157 /** Set when a volume button is clicked to take photo */
158 private boolean mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800159
Michael Kolb8872c232013-01-29 10:33:22 -0800160 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
Puneet Lall70a96522014-09-11 17:33:21 -0700166 /*
167 * If true, attempts to start the preview will be denied. This ensures that
168 * we never call startPreview multiple times when making changes to
169 * settings.
170 */
171 private boolean mStartPreviewLock = false;
172
Michael Kolb8872c232013-01-29 10:33:22 -0800173 // The degrees of the device rotated clockwise from its natural orientation.
174 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800175
176 private static final String sTempCropFilename = "crop-temp";
177
Michael Kolb8872c232013-01-29 10:33:22 -0800178 private boolean mFaceDetectionStarted = false;
179
Michael Kolb8872c232013-01-29 10:33:22 -0800180 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
181 private String mCropValue;
182 private Uri mSaveUri;
183
Ruben Brunkd217ed02013-10-08 23:31:13 -0700184 private Uri mDebugUri;
185
Angus Kongce5480e2013-01-29 17:43:48 -0800186 // We use a queue to generated names of the images to be used later
187 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800188 private NamedImages mNamedImages;
189
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800190 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800191 @Override
192 public void run() {
193 onShutterButtonClick();
194 }
195 };
196
Michael Kolb8872c232013-01-29 10:33:22 -0800197 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800198 * An unpublished intent flag requesting to return as soon as capturing is
199 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800200 */
201 private static final String EXTRA_QUICK_CAPTURE =
202 "android.intent.extra.quickCapture";
203
204 // The display rotation in degrees. This is only valid when mCameraState is
205 // not PREVIEW_STOPPED.
206 private int mDisplayRotation;
207 // The value for android.hardware.Camera.setDisplayOrientation.
208 private int mCameraDisplayOrientation;
209 // The value for UI components like indicators.
210 private int mDisplayOrientation;
Angus Kong831347d2014-06-16 16:07:28 -0700211 // The value for cameradevice.CameraSettings.setPhotoRotationDegrees.
Michael Kolb8872c232013-01-29 10:33:22 -0800212 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700213 // Indicates whether we are using front camera
214 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800215 private boolean mFirstTimeInitialized;
216 private boolean mIsImageCaptureIntent;
217
Michael Kolb8872c232013-01-29 10:33:22 -0800218 private int mCameraState = PREVIEW_STOPPED;
219 private boolean mSnapshotOnIdle = false;
220
221 private ContentResolver mContentResolver;
222
Doris Liu2b906b82013-12-10 16:34:08 -0800223 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800224
Michael Kolb8872c232013-01-29 10:33:22 -0800225 private final PostViewPictureCallback mPostViewPictureCallback =
226 new PostViewPictureCallback();
227 private final RawPictureCallback mRawPictureCallback =
228 new RawPictureCallback();
229 private final AutoFocusCallback mAutoFocusCallback =
230 new AutoFocusCallback();
231 private final Object mAutoFocusMoveCallback =
232 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700233 ? new AutoFocusMoveCallback()
234 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800235
236 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
237
238 private long mFocusStartTime;
239 private long mShutterCallbackTime;
240 private long mPostViewPictureCallbackTime;
241 private long mRawPictureCallbackTime;
242 private long mJpegPictureCallbackTime;
243 private long mOnResumeTime;
244 private byte[] mJpegImageData;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700245 /** Touch coordinate for shutter button press. */
246 private TouchCoordinate mShutterTouchCoordinate;
247
Michael Kolb8872c232013-01-29 10:33:22 -0800248
249 // These latency time are for the CameraLatency test.
250 public long mAutoFocusTime;
251 public long mShutterLag;
252 public long mShutterToPictureDisplayedTime;
253 public long mPictureDisplayedToJpegCallbackTime;
254 public long mJpegCallbackFinishTime;
255 public long mCaptureStartTime;
256
257 // This handles everything about focus.
258 private FocusOverlayManager mFocusManager;
259
Doris Liubd1b8f92014-01-03 17:59:51 -0800260 private final int mGcamModeIndex;
Andy Huibersa31162c2014-09-02 11:27:11 -0700261 private SoundPlayer mCountdownSoundPlayer;
Doris Liubd1b8f92014-01-03 17:59:51 -0800262
Angus Kong6607dae2014-06-10 16:07:45 -0700263 private CameraCapabilities.SceneMode mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800264
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100265 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800266
Michael Kolb8872c232013-01-29 10:33:22 -0800267 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700268 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800269 private final float[] mGData = new float[3];
270 private final float[] mMData = new float[3];
271 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700272 private int mHeading = -1;
273
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800274 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700275 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700276
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800277 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800278 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800279 @Override
280 public void onMediaSaved(Uri uri) {
281 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700282 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800283 }
284 }
285 };
Angus Kong454d63f2014-05-06 14:45:59 -0700286 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800287
Angus Kong0b9eb5b2014-04-30 15:03:33 -0700288 private final Runnable mResumeTaskRunnable = new Runnable() {
289 @Override
290 public void run() {
291 onResumeTasks();
292 }
293 };
294
Doris Liudb8f9752014-05-12 15:25:13 -0700295 /**
Angus Kong3699c412014-05-23 15:31:09 -0700296 * We keep the flash setting before entering scene modes (HDR)
297 * and restore it after HDR is off.
298 */
299 private String mFlashModeBeforeSceneMode;
300
301 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700302 * This callback gets called when user select whether or not to
303 * turn on geo-tagging.
304 */
305 public interface LocationDialogCallback {
306 /**
307 * Gets called after user selected/unselected geo-tagging feature.
308 *
309 * @param selected whether or not geo-tagging feature is selected
310 */
311 public void onLocationTaggingSelected(boolean selected);
312 }
313
314 /**
315 * This callback defines the text that is shown in the aspect ratio selection
316 * dialog, provides the current aspect ratio, and gets notified when user changes
317 * aspect ratio selection in the dialog.
318 */
319 public interface AspectRatioDialogCallback {
320 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700321 * Returns current aspect ratio that is being used to set as default.
322 */
323 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
324
325 /**
326 * Gets notified when user has made the aspect ratio selection.
327 *
328 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700329 * @param dialogHandlingFinishedRunnable runnable to run when the operations
330 * needed to handle changes from dialog
331 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700332 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700333 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
334 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700335 }
336
Angus Kongdcccc512013-08-08 17:06:03 -0700337 private void checkDisplayRotation() {
338 // Set the display orientation if display rotation has changed.
339 // Sometimes this happens when the device is held upside
340 // down and camera app is opened. Rotation animation will
341 // take some time and the rotation value we have got may be
342 // wrong. Framework does not have a callback for this now.
343 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
344 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800345 }
Angus Kongdcccc512013-08-08 17:06:03 -0700346 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
347 mHandler.postDelayed(new Runnable() {
348 @Override
349 public void run() {
350 checkDisplayRotation();
351 }
352 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800353 }
354 }
355
356 /**
357 * This Handler is used to post message back onto the main thread of the
358 * application
359 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100360 private static class MainHandler extends Handler {
361 private final WeakReference<PhotoModule> mModule;
362
363 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800364 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100365 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800366 }
367
Michael Kolb8872c232013-01-29 10:33:22 -0800368 @Override
369 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100370 PhotoModule module = mModule.get();
371 if (module == null) {
372 return;
373 }
Michael Kolb8872c232013-01-29 10:33:22 -0800374 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800375 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100376 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800377 break;
378 }
379
Angus Kong13e87c42013-11-25 10:02:47 -0800380 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100381 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800382 break;
383 }
Michael Kolb8872c232013-01-29 10:33:22 -0800384 }
385 }
386 }
387
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700388 private void switchToGcamCapture() {
389 if (mActivity != null && mGcamModeIndex != 0) {
390 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700391 settingsManager.set(SettingsManager.SCOPE_GLOBAL,
392 Keys.KEY_CAMERA_HDR_PLUS, true);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700393
394 // Disable the HDR+ button to prevent callbacks from being
395 // queued before the correct callback is attached to the button
396 // in the new module. The new module will set the enabled/disabled
397 // of this button when the module's preferred camera becomes available.
398 ButtonManager buttonManager = mActivity.getButtonManager();
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700399
400 buttonManager.disableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700401
Seth Raphael274f6e92014-05-21 17:11:53 -0700402 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
403
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700404 // Do not post this to avoid this module switch getting interleaved with
405 // other button callbacks.
406 mActivity.onModeSelected(mGcamModeIndex);
Spike Sprague66d3d0d2014-08-18 14:11:33 -0700407
408 buttonManager.enableButtonClick(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700409 }
410 }
411
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800412 /**
413 * Constructs a new photo module.
414 */
Angus Kongc4e66562013-11-22 23:03:21 -0800415 public PhotoModule(AppController app) {
416 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800417 mGcamModeIndex = app.getAndroidContext().getResources()
418 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800419 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700420
Michael Kolb8872c232013-01-29 10:33:22 -0800421 @Override
Spike Sprague159e6e92014-05-27 18:26:30 -0700422 public String getPeekAccessibilityString() {
423 return mAppController.getAndroidContext()
424 .getResources().getString(R.string.photo_accessibility_peek);
425 }
426
427 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -0700428 public String getModuleStringIdentifier() {
429 return PHOTO_MODULE_STRING_ID;
430 }
431
432 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100433 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
434 mActivity = activity;
435 // TODO: Need to look at the controller interface to see if we can get
436 // rid of passing in the activity directly.
437 mAppController = mActivity;
438
439 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
440 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800441
442 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700443 mCameraId = settingsManager.getInteger(mAppController.getModuleScope(),
444 Keys.KEY_CAMERA_ID);
Michael Kolb8872c232013-01-29 10:33:22 -0800445
Doris Liudb8f9752014-05-12 15:25:13 -0700446 // TODO: Move this to SettingsManager as a part of upgrade procedure.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700447 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
448 Keys.KEY_USER_SELECTED_ASPECT_RATIO)) {
Doris Liudb8f9752014-05-12 15:25:13 -0700449 // Switch to back camera to set aspect ratio.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700450 mCameraId = settingsManager.getIntegerDefault(Keys.KEY_CAMERA_ID);
Doris Liudb8f9752014-05-12 15:25:13 -0700451 }
452
Michael Kolb8872c232013-01-29 10:33:22 -0800453 mContentResolver = mActivity.getContentResolver();
454
Michael Kolb8872c232013-01-29 10:33:22 -0800455 // Surface texture is from camera screen nail and startPreview needs it.
456 // This must be done before startPreview.
457 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800458
Michael Kolb8872c232013-01-29 10:33:22 -0800459 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800460 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700461 mUI.setCountdownFinishedListener(this);
Andy Huibersa31162c2014-09-02 11:27:11 -0700462 mCountdownSoundPlayer = new SoundPlayer(mAppController.getAndroidContext());
Doris Liu6c751642014-05-05 18:43:26 -0700463
464 // TODO: Make this a part of app controller API.
465 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
466 cancelButton.setOnClickListener(new View.OnClickListener() {
467 @Override
468 public void onClick(View view) {
469 cancelCountDown();
470 }
471 });
472 }
473
474 private void cancelCountDown() {
475 if (mUI.isCountingDown()) {
476 // Cancel on-going countdown.
477 mUI.cancelCountDown();
478 }
479 mAppController.getCameraAppUI().transitionToCapture();
480 mAppController.getCameraAppUI().showModeOptions();
Michael Kolbd6954f32013-03-08 20:43:01 -0800481 }
482
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800483 @Override
484 public boolean isUsingBottomBar() {
485 return true;
486 }
487
Michael Kolbd6954f32013-03-08 20:43:01 -0800488 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800489 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800490 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800491 setupCaptureParams();
492 }
493 }
494
495 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800496 mAppController.onPreviewStarted();
Alan Newbergerffc395d2014-09-10 13:07:18 -0700497 mAppController.setShutterEnabled(true);
Michael Kolbd6954f32013-03-08 20:43:01 -0800498 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800499 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700500 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800501 }
502
Doris Liudb8f9752014-05-12 15:25:13 -0700503 /**
504 * Prompt the user to pick to record location and choose aspect ratio for the
505 * very first run of camera only.
506 */
507 private void settingsFirstRun() {
508 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100509
Doris Liudb8f9752014-05-12 15:25:13 -0700510 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800511 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800512 }
Doris Liudb8f9752014-05-12 15:25:13 -0700513
Erin Dahlgren6190c362014-06-13 14:12:08 -0700514 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SCOPE_GLOBAL,
515 Keys.KEY_RECORD_LOCATION);
Doris Liudb8f9752014-05-12 15:25:13 -0700516 boolean aspectRatioPrompt = !settingsManager.getBoolean(
Erin Dahlgren6190c362014-06-13 14:12:08 -0700517 SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO);
Doris Liudb8f9752014-05-12 15:25:13 -0700518 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800519 return;
520 }
Doris Liudb8f9752014-05-12 15:25:13 -0700521
Michael Kolb8872c232013-01-29 10:33:22 -0800522 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800523 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800524 if (backCameraId == -1) {
525 // If there is no back camera, do not show the prompt.
526 return;
527 }
Doris Liudb8f9752014-05-12 15:25:13 -0700528
529 if (locationPrompt) {
530 // Show both location and aspect ratio selection dialog.
531 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700532 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700533 public void onLocationTaggingSelected(boolean selected) {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700534 Keys.setLocation(mActivity.getSettingsManager(), selected,
535 mActivity.getLocationManager());
Doris Liudb8f9752014-05-12 15:25:13 -0700536 }
537 }, createAspectRatioDialogCallback());
538 } else {
539 // App upgrade. Only show aspect ratio selection.
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700540 boolean wasShown = mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
541 if (!wasShown) {
542 // If the dialog was not shown, set this flag to true so that we
543 // never have to check for it again. It means that we don't need
544 // to show the dialog on this device.
545 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
546 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
547 }
Doris Liudb8f9752014-05-12 15:25:13 -0700548 }
549 }
550
551 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -0700552 Size currentSize = mCameraSettings.getCurrentPhotoSize();
Doris Liudb8f9752014-05-12 15:25:13 -0700553 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
554 if (aspectRatio < 1f) {
555 aspectRatio = 1 / aspectRatio;
556 }
557 final AspectRatioSelector.AspectRatio currentAspectRatio;
558 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
559 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
560 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
561 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
562 } else {
563 // TODO: Log error and not show dialog.
564 return null;
565 }
566
Angus Kong6607dae2014-06-10 16:07:45 -0700567 List<Size> sizes = mCameraCapabilities.getSupportedPhotoSizes();
Doris Liudb8f9752014-05-12 15:25:13 -0700568 List<Size> pictureSizes = ResolutionUtil
569 .getDisplayableSizesFromSupported(sizes, true);
570
571 // This logic below finds the largest resolution for each aspect ratio.
572 // TODO: Move this somewhere that can be shared with SettingsActivity
573 int aspectRatio4x3Resolution = 0;
574 int aspectRatio16x9Resolution = 0;
575 Size largestSize4x3 = new Size(0, 0);
576 Size largestSize16x9 = new Size(0, 0);
577 for (Size size : pictureSizes) {
578 float pictureAspectRatio = (float) size.width() / (float) size.height();
579 pictureAspectRatio = pictureAspectRatio < 1 ?
580 1f / pictureAspectRatio : pictureAspectRatio;
581 int resolution = size.width() * size.height();
582 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
583 if (resolution > aspectRatio4x3Resolution) {
584 aspectRatio4x3Resolution = resolution;
585 largestSize4x3 = size;
586 }
587 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
588 if (resolution > aspectRatio16x9Resolution) {
589 aspectRatio16x9Resolution = resolution;
590 largestSize16x9 = size;
591 }
592 }
593 }
Doris Liudb8f9752014-05-12 15:25:13 -0700594
Doris Liu08b0cdd2014-05-13 19:19:55 -0700595 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
596 final Size size4x3ToSelect = largestSize4x3;
597 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700598
Doris Liudb8f9752014-05-12 15:25:13 -0700599 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700600
601 @Override
602 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
603 return currentAspectRatio;
604 }
605
606 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700607 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
608 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700609 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700610 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700611 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
612 Keys.KEY_PICTURE_SIZE_BACK,
613 largestSize4x3Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700614 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700615 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Erin Dahlgren6190c362014-06-13 14:12:08 -0700616 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
617 Keys.KEY_PICTURE_SIZE_BACK,
618 largestSize16x9Text);
Doris Liudb8f9752014-05-12 15:25:13 -0700619 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700620 mActivity.getSettingsManager().set(SettingsManager.SCOPE_GLOBAL,
621 Keys.KEY_USER_SELECTED_ASPECT_RATIO, true);
622 String aspectRatio = mActivity.getSettingsManager().getString(
623 SettingsManager.SCOPE_GLOBAL,
624 Keys.KEY_USER_SELECTED_ASPECT_RATIO);
625 Log.e(TAG, "aspect ratio after setting it to true=" + aspectRatio);
Doris Liudb8f9752014-05-12 15:25:13 -0700626 if (newAspectRatio != currentAspectRatio) {
Doris Liudb8f9752014-05-12 15:25:13 -0700627 stopPreview();
628 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700629 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
630 } else {
631 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700632 }
633 }
634 };
635 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700636 }
Michael Kolb8872c232013-01-29 10:33:22 -0800637
ztenghui7b265a62013-09-09 14:58:44 -0700638 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700639 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800640 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700641 }
642
643 @Override
644 public void onPreviewUIDestroyed() {
645 if (mCameraDevice == null) {
646 return;
647 }
648 mCameraDevice.setPreviewTexture(null);
649 stopPreview();
650 }
651
Doris Liu1dfe7822013-12-12 00:02:08 -0800652 @Override
653 public void startPreCaptureAnimation() {
654 mAppController.startPreCaptureAnimation();
655 }
656
Michael Kolbd6954f32013-03-08 20:43:01 -0800657 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800658 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800659 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800660 }
661
Michael Kolbd6954f32013-03-08 20:43:01 -0800662 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800663 if (mPaused) {
664 return;
665 }
Doris Liu6c751642014-05-05 18:43:26 -0700666 cancelCountDown();
Doris Liu6809eb82014-05-21 13:16:59 -0700667
668 mAppController.freezeScreenUntilPreviewReady();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800669 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800670
Alan Newbergerd41766f2014-04-09 18:25:34 -0700671 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800672 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800673 mCameraId = mPendingSwitchCameraId;
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700674
Erin Dahlgren6190c362014-06-13 14:12:08 -0700675 settingsManager.set(mAppController.getModuleScope(), Keys.KEY_CAMERA_ID, mCameraId);
Sol Boucher44ce4b22014-08-04 23:41:38 -0700676 requestCameraOpen();
Michael Kolbd6954f32013-03-08 20:43:01 -0800677 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800678 if (mFocusManager != null) {
679 mFocusManager.removeMessages();
680 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800681
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700682 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700683 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700684 // Start switch camera animation. Post a message because
685 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800686 }
687
Sol Boucher44ce4b22014-08-04 23:41:38 -0700688 /**
689 * Uses the {@link CameraProvider} to open the currently-selected camera
690 * device, using {@link GservicesHelper} to choose between API-1 and API-2.
691 */
692 private void requestCameraOpen() {
Alan Newberger29a009c2014-09-05 12:47:42 -0700693 Log.v(TAG, "requestCameraOpen");
Sol Boucher44ce4b22014-08-04 23:41:38 -0700694 mActivity.getCameraProvider().requestCamera(mCameraId,
695 GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity));
696 }
697
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800698 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100699 new ButtonManager.ButtonCallback() {
700 @Override
701 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800702 // At the time this callback is fired, the camera id
703 // has be set to the desired camera.
704
Angus Kong97e282a2014-03-04 18:44:49 -0800705 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100706 return;
707 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800708 // If switching to back camera, and HDR+ is still on,
709 // switch back to gcam, otherwise handle callback normally.
710 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700711 if (Keys.isCameraBackFacing(settingsManager,
712 mAppController.getModuleScope())) {
713 if (Keys.requestsReturnToHdrPlus(settingsManager,
714 mAppController.getModuleScope())) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700715 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800716 return;
717 }
718 }
719
Sascha Haeberlingde303232014-02-07 02:30:53 +0100720 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800721
Alan Newbergerd41766f2014-04-09 18:25:34 -0700722 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100723 // We need to keep a preview frame for the animation before
724 // releasing the camera. This will trigger
725 // onPreviewTextureCopied.
726 // TODO: Need to animate the camera switch
727 switchCamera();
728 }
729 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800730
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800731 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100732 new ButtonManager.ButtonCallback() {
733 @Override
734 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700735 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700736 if (GcamHelper.hasGcamAsSeparateModule()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100737 // Set the camera setting to default backfacing.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700738 settingsManager.setToDefault(mAppController.getModuleScope(),
739 Keys.KEY_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700740 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100741 } else {
Erin Dahlgren6190c362014-06-13 14:12:08 -0700742 if (Keys.isHdrOn(settingsManager)) {
Angus Kong831347d2014-06-16 16:07:28 -0700743 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
744 mCameraCapabilities.getStringifier().stringify(
745 CameraCapabilities.SceneMode.HDR));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700746 } else {
Angus Kong831347d2014-06-16 16:07:28 -0700747 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_SCENE_MODE,
748 mCameraCapabilities.getStringifier().stringify(
749 CameraCapabilities.SceneMode.AUTO));
Doris Liu8ad8ad42014-03-27 14:43:31 -0700750 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100751 updateParametersSceneMode();
Angus Kong6607dae2014-06-10 16:07:45 -0700752 mCameraDevice.applySettings(mCameraSettings);
Doris Liu8ad8ad42014-03-27 14:43:31 -0700753 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100754 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800755 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100756 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800757
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800758 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
759 @Override
760 public void onClick(View v) {
761 onCaptureCancelled();
762 }
763 };
764
765 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
766 @Override
767 public void onClick(View v) {
768 onCaptureDone();
769 }
770 };
771
772 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
773 @Override
774 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800775 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800776 onCaptureRetake();
777 }
778 };
779
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800780 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700781 public void hardResetSettings(SettingsManager settingsManager) {
Erin Dahlgrene1547932014-06-23 15:17:07 -0700782 // PhotoModule should hard reset HDR+ to off,
783 // and HDR to off if HDR+ is supported.
Erin Dahlgren6190c362014-06-13 14:12:08 -0700784 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, false);
Sascha Haeberling4c1bffe2014-08-21 10:01:00 -0700785 if (GcamHelper.hasGcamAsSeparateModule()) {
Erin Dahlgren21a62362014-06-24 10:13:01 -0700786 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, false);
Erin Dahlgrene1547932014-06-23 15:17:07 -0700787 }
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700788 }
789
790 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800791 public HardwareSpec getHardwareSpec() {
Angus Kong831347d2014-06-16 16:07:28 -0700792 return (mCameraSettings != null ?
793 new HardwareSpecImpl(getCameraProvider(), mCameraCapabilities) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800794 }
795
796 @Override
797 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
798 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
799
800 bottomBarSpec.enableCamera = true;
801 bottomBarSpec.cameraCallback = mCameraCallback;
Erin Dahlgren6190c362014-06-13 14:12:08 -0700802 bottomBarSpec.enableFlash = !mAppController.getSettingsManager()
803 .getBoolean(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR);
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800804 bottomBarSpec.enableHdr = true;
805 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800806 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700807 if (mCameraCapabilities != null) {
808 bottomBarSpec.enableExposureCompensation = true;
809 bottomBarSpec.exposureCompensationSetCallback =
810 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
811 @Override
812 public void setExposure(int value) {
813 setExposureCompensation(value);
814 }
815 };
816 bottomBarSpec.minExposureCompensation =
817 mCameraCapabilities.getMinExposureCompensation();
818 bottomBarSpec.maxExposureCompensation =
819 mCameraCapabilities.getMaxExposureCompensation();
820 bottomBarSpec.exposureCompensationStep =
821 mCameraCapabilities.getExposureCompensationStep();
822 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800823
Doris Liu6c751642014-05-05 18:43:26 -0700824 bottomBarSpec.enableSelfTimer = true;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700825 bottomBarSpec.showSelfTimer = true;
Doris Liu6c751642014-05-05 18:43:26 -0700826
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800827 if (isImageCaptureIntent()) {
828 bottomBarSpec.showCancel = true;
829 bottomBarSpec.cancelCallback = mCancelCallback;
830 bottomBarSpec.showDone = true;
831 bottomBarSpec.doneCallback = mDoneCallback;
832 bottomBarSpec.showRetake = true;
833 bottomBarSpec.retakeCallback = mRetakeCallback;
834 }
835
836 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800837 }
838
Michael Kolbd6954f32013-03-08 20:43:01 -0800839 // either open a new camera or switch cameras
840 private void openCameraCommon() {
Angus Kong6607dae2014-06-10 16:07:45 -0700841 mUI.onCameraOpened(mCameraCapabilities, mCameraSettings);
Angus Kong0fb819b2013-10-08 13:44:19 -0700842 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800843 // Set hdr plus to default: off.
844 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -0700845 settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL,
846 Keys.KEY_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700847 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800848 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800849 }
850
ztenghui7b265a62013-09-09 14:58:44 -0700851 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800852 public void updatePreviewAspectRatio(float aspectRatio) {
853 mAppController.updatePreviewAspectRatio(aspectRatio);
854 }
855
Michael Kolb8872c232013-01-29 10:33:22 -0800856 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800857 SettingsManager settingsManager = mActivity.getSettingsManager();
858 if (settingsManager == null) {
859 Log.e(TAG, "Settings manager is null!");
860 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800861 }
Erin Dahlgren6190c362014-06-13 14:12:08 -0700862 settingsManager.setToDefault(mAppController.getCameraScope(),
863 Keys.KEY_EXPOSURE);
Michael Kolb8872c232013-01-29 10:33:22 -0800864 }
865
Michael Kolb8872c232013-01-29 10:33:22 -0800866 // Snapshots can only be taken after this is called. It should be called
867 // once only. We could have done these things in onCreate() but we want to
868 // make preview screen appear as soon as possible.
869 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700870 if (mFirstTimeInitialized || mPaused) {
871 return;
872 }
Michael Kolb8872c232013-01-29 10:33:22 -0800873
Michael Kolbd6954f32013-03-08 20:43:01 -0800874 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800875
Angus Kong86d36312013-01-31 18:22:44 -0800876 // We set the listener only when both service and shutterbutton
877 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800878 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800879
Michael Kolb8872c232013-01-29 10:33:22 -0800880 mNamedImages = new NamedImages();
881
882 mFirstTimeInitialized = true;
883 addIdleHandler();
884
Spike Spraguee6374b72014-04-25 17:24:32 -0700885 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800886 }
887
Michael Kolbd6954f32013-03-08 20:43:01 -0800888 // If the activity is paused and resumed, this method will be called in
889 // onResume.
890 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800891 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800892 mNamedImages = new NamedImages();
Angus Kong6607dae2014-06-10 16:07:45 -0700893 mUI.initializeSecondTime(mCameraCapabilities, mCameraSettings);
Michael Kolbd6954f32013-03-08 20:43:01 -0800894 }
895
Michael Kolb8872c232013-01-29 10:33:22 -0800896 private void addIdleHandler() {
897 MessageQueue queue = Looper.myQueue();
898 queue.addIdleHandler(new MessageQueue.IdleHandler() {
899 @Override
900 public boolean queueIdle() {
901 Storage.ensureOSXCompatible();
902 return false;
903 }
904 });
905 }
906
Michael Kolb8872c232013-01-29 10:33:22 -0800907 @Override
908 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800909 if (mFaceDetectionStarted) {
910 return;
911 }
Angus Kong88289042014-04-22 16:39:42 -0700912 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800913 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700914 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700915 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800916 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700917 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800918 }
919 }
920
Michael Kolb8872c232013-01-29 10:33:22 -0800921 @Override
922 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800923 if (!mFaceDetectionStarted) {
924 return;
925 }
Angus Kong88289042014-04-22 16:39:42 -0700926 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800927 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700928 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800929 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800930 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700931 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800932 }
933 }
934
Michael Kolb8872c232013-01-29 10:33:22 -0800935 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700936 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700937
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800938 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700939
Sascha Haeberling37f36112013-08-06 14:31:52 -0700940 public ShutterCallback(boolean needsAnimation) {
941 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700942 }
943
Michael Kolb8872c232013-01-29 10:33:22 -0800944 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700945 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800946 mShutterCallbackTime = System.currentTimeMillis();
947 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
948 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700949 if (mNeedsAnimation) {
950 mActivity.runOnUiThread(new Runnable() {
951 @Override
952 public void run() {
953 animateAfterShutter();
954 }
955 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700956 }
Michael Kolb8872c232013-01-29 10:33:22 -0800957 }
958 }
959
Angus Kong9ef99252013-07-18 18:04:19 -0700960 private final class PostViewPictureCallback
961 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800962 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800963 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800964 mPostViewPictureCallbackTime = System.currentTimeMillis();
965 Log.v(TAG, "mShutterToPostViewCallbackTime = "
966 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
967 + "ms");
968 }
969 }
970
Angus Kong9ef99252013-07-18 18:04:19 -0700971 private final class RawPictureCallback
972 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800973 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800974 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800975 mRawPictureCallbackTime = System.currentTimeMillis();
976 Log.v(TAG, "mShutterToRawCallbackTime = "
977 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
978 }
979 }
980
Angus Kong454d63f2014-05-06 14:45:59 -0700981 private static class ResizeBundle {
982 byte[] jpegData;
983 float targetAspectRatio;
984 ExifInterface exif;
985 }
986
987 /**
988 * @return Cropped image if the target aspect ratio is larger than the jpeg
989 * aspect ratio on the long axis. The original jpeg otherwise.
990 */
991 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
992
993 final byte[] jpegData = dataBundle.jpegData;
994 final ExifInterface exif = dataBundle.exif;
995 float targetAspectRatio = dataBundle.targetAspectRatio;
996
997 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
998 int originalWidth = original.getWidth();
999 int originalHeight = original.getHeight();
1000 int newWidth;
1001 int newHeight;
1002
1003 if (originalWidth > originalHeight) {
1004 newHeight = (int) (originalWidth / targetAspectRatio);
1005 newWidth = originalWidth;
1006 } else {
1007 newWidth = (int) (originalHeight / targetAspectRatio);
1008 newHeight = originalHeight;
1009 }
1010 int xOffset = (originalWidth - newWidth)/2;
1011 int yOffset = (originalHeight - newHeight)/2;
1012
Andy Huibers0f54d282014-09-09 12:52:29 -07001013 // For some reason L needs this to work.
1014 // This code is only run on the Nexus 5.
1015 // TODO: Determine why this is needed.
1016 if (Build.VERSION.SDK_INT >= 21 || Build.VERSION.CODENAME.equals("L")) {
1017 Log.v(TAG,"xOffset = " + xOffset);
1018 Log.v(TAG,"yOffset = " + yOffset);
1019 xOffset *= 2;
1020 yOffset = 0;
1021 Log.v(TAG,"new xOffset = " + xOffset);
1022 Log.v(TAG,"new yOffset = " + yOffset);
1023 }
1024
Angus Kong454d63f2014-05-06 14:45:59 -07001025 if (xOffset < 0 || yOffset < 0) {
1026 return dataBundle;
1027 }
1028
1029 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
1030 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
1031 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
1032
1033 ByteArrayOutputStream stream = new ByteArrayOutputStream();
1034
1035 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
1036 dataBundle.jpegData = stream.toByteArray();
1037 return dataBundle;
1038 }
1039
Angus Kong9ef99252013-07-18 18:04:19 -07001040 private final class JpegPictureCallback
1041 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001042 Location mLocation;
1043
1044 public JpegPictureCallback(Location loc) {
1045 mLocation = loc;
1046 }
1047
1048 @Override
Angus Kong454d63f2014-05-06 14:45:59 -07001049 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07001050 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001051 if (mPaused) {
1052 return;
1053 }
Doris Liu6432cd62013-06-13 17:20:31 -07001054 if (mIsImageCaptureIntent) {
1055 stopPreview();
1056 }
Angus Kong6607dae2014-06-10 16:07:45 -07001057 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001058 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001059 }
1060
1061 mJpegPictureCallbackTime = System.currentTimeMillis();
1062 // If postview callback has arrived, the captured image is displayed
1063 // in postview callback. If not, the captured image is displayed in
1064 // raw picture callback.
1065 if (mPostViewPictureCallbackTime != 0) {
1066 mShutterToPictureDisplayedTime =
1067 mPostViewPictureCallbackTime - mShutterCallbackTime;
1068 mPictureDisplayedToJpegCallbackTime =
1069 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1070 } else {
1071 mShutterToPictureDisplayedTime =
1072 mRawPictureCallbackTime - mShutterCallbackTime;
1073 mPictureDisplayedToJpegCallbackTime =
1074 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1075 }
1076 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1077 + mPictureDisplayedToJpegCallbackTime + "ms");
1078
Michael Kolb8872c232013-01-29 10:33:22 -08001079 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1080 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001081 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001082 }
1083
Angus Kong454d63f2014-05-06 14:45:59 -07001084 long now = System.currentTimeMillis();
1085 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1086 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1087 mJpegPictureCallbackTime = 0;
1088
1089 final ExifInterface exif = Exif.getExif(originalJpegData);
1090
1091 if (mShouldResizeTo16x9) {
1092 final ResizeBundle dataBundle = new ResizeBundle();
1093 dataBundle.jpegData = originalJpegData;
1094 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1095 dataBundle.exif = exif;
1096 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1097
1098 @Override
1099 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1100 return cropJpegDataToAspectRatio(resizeBundles[0]);
1101 }
1102
1103 @Override
1104 protected void onPostExecute(ResizeBundle result) {
1105 saveFinalPhoto(result.jpegData, result.exif, camera);
1106 }
1107 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1108
1109 } else {
1110 saveFinalPhoto(originalJpegData, exif, camera);
1111 }
1112 }
1113
1114 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1115
Doris Liu36e56fb2013-09-11 17:38:08 -07001116 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001117
Sol Boucher2192fba2014-08-19 17:24:07 -07001118 float zoomValue = 1.0f;
Angus Kong88289042014-04-22 16:39:42 -07001119 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07001120 zoomValue = mCameraSettings.getCurrentZoomRatio();
Andy Huiberse08bc042014-04-11 19:26:47 -07001121 }
Angus Kong6607dae2014-06-10 16:07:45 -07001122 boolean hdrOn = CameraCapabilities.SceneMode.HDR == mSceneMode;
Andy Huibers203abe52014-05-19 13:59:01 -07001123 String flashSetting =
Erin Dahlgren6190c362014-06-13 14:12:08 -07001124 mActivity.getSettingsManager().getString(mAppController.getCameraScope(),
1125 Keys.KEY_FLASH_MODE);
1126 boolean gridLinesOn = Keys.areGridLinesOn(mActivity.getSettingsManager());
Andy Huibers10c58162014-03-29 14:06:54 -07001127 UsageStatistics.instance().photoCaptureDoneEvent(
1128 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1129 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers547d7c82014-05-20 23:03:18 -07001130 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001131 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1132 mShutterTouchCoordinate = null;
1133 mVolumeButtonClickedFlag = false;
Ruben Brunkd217ed02013-10-08 23:31:13 -07001134
Ruben Brunkd7488272013-10-10 18:45:53 -07001135 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001136 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001137 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1138 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001139 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001140 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1141 width = exifWidth;
1142 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001143 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001144 Size s;
Angus Kong6607dae2014-06-10 16:07:45 -07001145 s = mCameraSettings.getCurrentPhotoSize();
Angus Kong454d63f2014-05-06 14:45:59 -07001146 if ((mJpegRotation + orientation) % 180 == 0) {
1147 width = s.width();
1148 height = s.height();
1149 } else {
1150 width = s.height();
1151 height = s.width();
1152 }
Michael Kolb8872c232013-01-29 10:33:22 -08001153 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001154 NamedEntity name = mNamedImages.getNextNameEntity();
1155 String title = (name == null) ? null : name.title;
1156 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001157
1158 // Handle debug mode outputs
1159 if (mDebugUri != null) {
1160 // If using a debug uri, save jpeg there.
1161 saveToDebugUri(jpegData);
1162
1163 // Adjust the title of the debug image shown in mediastore.
1164 if (title != null) {
1165 title = DEBUG_IMAGE_PREFIX + title;
1166 }
1167 }
1168
Michael Kolb8872c232013-01-29 10:33:22 -08001169 if (title == null) {
1170 Log.e(TAG, "Unbalanced name/data pair");
1171 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001172 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001173 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001174 }
Angus Kong0d00a892013-03-26 11:40:40 -07001175 if (mHeading >= 0) {
1176 // heading direction has been updated by the sensor.
1177 ExifTag directionRefTag = exif.buildTag(
1178 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1179 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1180 ExifTag directionTag = exif.buildTag(
1181 ExifInterface.TAG_GPS_IMG_DIRECTION,
1182 new Rational(mHeading, 1));
1183 exif.setTag(directionRefTag);
1184 exif.setTag(directionTag);
1185 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001186 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001187 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001188 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001189 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001190 // Animate capture with real jpeg data instead of a preview
1191 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001192 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001193 } else {
1194 mJpegImageData = jpegData;
1195 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001196 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001197 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001198 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001199 }
1200 }
1201
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001202 // Send the taken photo to remote shutter listeners, if any are
1203 // registered.
Sascha Haeberling3c6de142014-07-14 12:23:36 -07001204 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001205
Michael Kolb8872c232013-01-29 10:33:22 -08001206 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001207 // latency. It's true that someone else could write to the SD card
1208 // in the mean time and fill it, but that could have happened
1209 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001210 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001211 }
1212 }
1213
Angus Kong9ef99252013-07-18 18:04:19 -07001214 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001215 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001216 public void onAutoFocus(boolean focused, CameraProxy camera) {
1217 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001218 if (mPaused) {
1219 return;
1220 }
Michael Kolb8872c232013-01-29 10:33:22 -08001221
1222 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001223 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001224 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001225 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001226 }
1227 }
1228
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001229 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001230 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001231 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001232 @Override
1233 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001234 boolean moving, CameraProxy camera) {
1235 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001236 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001237 }
1238 }
1239
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001240 /**
1241 * This class is just a thread-safe queue for name,date holder objects.
1242 */
1243 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001244 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001245
1246 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001247 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001248 }
1249
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001250 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001251 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001252 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001253 r.date = date;
1254 mQueue.add(r);
1255 }
1256
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001257 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001258 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001259 if (!mQueue.isEmpty()) {
1260 return mQueue.remove(0);
1261 }
Michael Kolb8872c232013-01-29 10:33:22 -08001262 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001263 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001264 }
1265
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001266 public static class NamedEntity {
1267 public String title;
1268 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001269 }
1270 }
1271
1272 private void setCameraState(int state) {
1273 mCameraState = state;
1274 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001275 case PREVIEW_STOPPED:
1276 case SNAPSHOT_IN_PROGRESS:
1277 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001278 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001279 break;
1280 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001281 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001282 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001283 }
1284 }
1285
Sascha Haeberling37f36112013-08-06 14:31:52 -07001286 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001287 // Only animate when in full screen capture mode
1288 // i.e. If monkey/a user swipes to the gallery during picture taking,
1289 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001290 if (!mIsImageCaptureIntent) {
1291 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001292 }
Michael Kolb8872c232013-01-29 10:33:22 -08001293 }
1294
1295 @Override
1296 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001297 // If we are already in the middle of taking a snapshot or the image
1298 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001299 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001300 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001301 return false;
1302 }
1303 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001304
Michael Kolb8872c232013-01-29 10:33:22 -08001305 mPostViewPictureCallbackTime = 0;
1306 mJpegImageData = null;
1307
Angus Kong6607dae2014-06-10 16:07:45 -07001308 final boolean animateBefore = (mSceneMode == CameraCapabilities.SceneMode.HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001309
1310 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001311 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001312 }
1313
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001314 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kong6607dae2014-06-10 16:07:45 -07001315 CameraUtil.setGpsParameters(mCameraSettings, loc);
1316 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb8872c232013-01-29 10:33:22 -08001317
Senpo Hubb1c72f2014-09-08 01:07:27 -07001318 // Set JPEG orientation. Even if screen UI is locked in portrait, camera orientation should
1319 // still match device orientation (e.g., users should always get landscape photos while
1320 // capturing by putting device in landscape.)
1321 int orientation = mActivity.isAutoRotateScreen() ? mDisplayRotation : mOrientation;
1322 Characteristics info = mActivity.getCameraProvider().getCharacteristics(mCameraId);
1323 mJpegRotation = info.getJpegOrientation(orientation);
1324 mCameraDevice.setJpegOrientation(mJpegRotation);
1325
Sascha Haeberling88901942013-08-28 17:49:00 -07001326 // We don't want user to press the button again while taking a
1327 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001328 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001329 mCameraDevice.takePicture(mHandler,
1330 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001331 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001332 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001333
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001334 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001335
1336 mFaceDetectionStarted = false;
1337 setCameraState(SNAPSHOT_IN_PROGRESS);
1338 return true;
1339 }
1340
1341 @Override
1342 public void setFocusParameters() {
1343 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1344 }
1345
Michael Kolbd6954f32013-03-08 20:43:01 -08001346 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001347 // If scene mode is set, we cannot set flash mode, white balance, and
1348 // focus mode, instead, we read it from driver
Angus Kong6607dae2014-06-10 16:07:45 -07001349 if (CameraCapabilities.SceneMode.AUTO != mSceneMode) {
1350 overrideCameraSettings(mCameraSettings.getCurrentFlashMode(),
1351 mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001352 }
1353 }
1354
Angus Kong6607dae2014-06-10 16:07:45 -07001355 private void overrideCameraSettings(CameraCapabilities.FlashMode flashMode,
1356 CameraCapabilities.FocusMode focusMode) {
1357 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrene419b192013-12-03 13:10:27 -08001358 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07001359 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
1360 stringifier.stringify(flashMode));
1361 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
1362 stringifier.stringify(focusMode));
Michael Kolb8872c232013-01-29 10:33:22 -08001363 }
1364
1365 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001366 public void onOrientationChanged(int orientation) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001367 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1368 return;
1369 }
Senpo Hubb1c72f2014-09-08 01:07:27 -07001370
1371 // TODO: Document orientation compute logic and unify them in OrientationManagerImpl.
1372 // b/17443789
1373 // Flip to counter-clockwise orientation.
1374 mOrientation = (360 - orientation) % 360;
Michael Kolb8872c232013-01-29 10:33:22 -08001375 }
1376
1377 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001378 public void onCameraAvailable(CameraProxy cameraProxy) {
Alan Newberger29a009c2014-09-05 12:47:42 -07001379 Log.v(TAG, "onCameraAvailable");
Angus Kong20fad242013-11-11 18:23:46 -08001380 if (mPaused) {
1381 return;
1382 }
1383 mCameraDevice = cameraProxy;
1384
1385 initializeCapabilities();
1386
1387 // Reset zoom value index.
Sol Boucher2192fba2014-08-19 17:24:07 -07001388 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001389 if (mFocusManager == null) {
1390 initializeFocusManager();
1391 }
Angus Kong831347d2014-06-16 16:07:28 -07001392 mFocusManager.updateCapabilities(mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001393
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001394 // Do camera parameter dependent initialization.
Angus Kong6607dae2014-06-10 16:07:45 -07001395 mCameraSettings = mCameraDevice.getSettings();
Puneet Lall70a96522014-09-11 17:33:21 -07001396 // HACK: The call to setCameraParameters(UPDATE_PARAM_ALL) may
1397 // eventually recurse back into startPreview().
1398 // To avoid calling startPreview() twice, first acquire
1399 // mStartPreviewLock.
1400 mStartPreviewLock = true;
1401 try {
1402 setCameraParameters(UPDATE_PARAM_ALL);
1403 // Set a listener which updates camera parameters based
1404 // on changed settings.
1405 SettingsManager settingsManager = mActivity.getSettingsManager();
1406 settingsManager.addListener(this);
1407 mCameraPreviewParamsReady = true;
1408 } finally {
1409 mStartPreviewLock = false;
1410 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001411
Angus Kong20fad242013-11-11 18:23:46 -08001412 startPreview();
1413
1414 onCameraOpened();
1415 }
1416
1417 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001418 public void onCaptureCancelled() {
1419 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1420 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001421 }
1422
Michael Kolbd6954f32013-03-08 20:43:01 -08001423 @Override
1424 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001425 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001426 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001427 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001428 mUI.hidePostCaptureAlert();
Spike Spragueb4a22222014-05-22 14:40:53 -07001429 mUI.hideIntentReviewImageView();
Michael Kolb8872c232013-01-29 10:33:22 -08001430 setupPreview();
1431 }
1432
Michael Kolbd6954f32013-03-08 20:43:01 -08001433 @Override
1434 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001435 if (mPaused) {
1436 return;
1437 }
1438
1439 byte[] data = mJpegImageData;
1440
1441 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001442 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001443 // caller specifies a "save uri" then write the data to its
1444 // stream. Otherwise, pass back a scaled down version of the bitmap
1445 // directly in the extras.
1446 if (mSaveUri != null) {
1447 OutputStream outputStream = null;
1448 try {
1449 outputStream = mContentResolver.openOutputStream(mSaveUri);
1450 outputStream.write(data);
1451 outputStream.close();
1452
Alan Newbergerf974ba72014-09-08 14:44:30 -07001453 Log.v(TAG, "saved result to URI: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001454 mActivity.setResultEx(Activity.RESULT_OK);
1455 mActivity.finish();
1456 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001457 Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001458 // ignore exception
1459 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001460 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001461 }
1462 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001463 ExifInterface exif = Exif.getExif(data);
1464 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001465 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1466 bitmap = CameraUtil.rotate(bitmap, orientation);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001467 Log.v(TAG, "inlined bitmap into capture intent result");
Michael Kolb8872c232013-01-29 10:33:22 -08001468 mActivity.setResultEx(Activity.RESULT_OK,
1469 new Intent("inline-data").putExtra("data", bitmap));
1470 mActivity.finish();
1471 }
1472 } else {
1473 // Save the image to a temp file and invoke the cropper
1474 Uri tempUri = null;
1475 FileOutputStream tempStream = null;
1476 try {
1477 File path = mActivity.getFileStreamPath(sTempCropFilename);
1478 path.delete();
1479 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1480 tempStream.write(data);
1481 tempStream.close();
1482 tempUri = Uri.fromFile(path);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001483 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename);
Michael Kolb8872c232013-01-29 10:33:22 -08001484 } catch (FileNotFoundException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001485 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001486 mActivity.setResultEx(Activity.RESULT_CANCELED);
1487 mActivity.finish();
1488 return;
1489 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001490 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001491 mActivity.setResultEx(Activity.RESULT_CANCELED);
1492 mActivity.finish();
1493 return;
1494 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001495 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001496 }
1497
1498 Bundle newExtras = new Bundle();
1499 if (mCropValue.equals("circle")) {
1500 newExtras.putString("circleCrop", "true");
1501 }
1502 if (mSaveUri != null) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001503 Log.v(TAG, "setting output of cropped file to: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001504 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1505 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001506 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001507 }
1508 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001509 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001510 }
1511
Sascha Haeberling37f36112013-08-06 14:31:52 -07001512 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001513 final String CROP_ACTION = "com.android.camera.action.CROP";
1514 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001515
1516 cropIntent.setData(tempUri);
1517 cropIntent.putExtras(newExtras);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001518 Log.v(TAG, "starting CROP intent for capture");
Michael Kolb8872c232013-01-29 10:33:22 -08001519 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1520 }
1521 }
1522
Michael Kolb8872c232013-01-29 10:33:22 -08001523 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001524 public void onShutterCoordinate(TouchCoordinate coord) {
1525 mShutterTouchCoordinate = coord;
1526 }
1527
1528 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001529 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001530 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001531 }
1532
1533 @Override
1534 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001535 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001536 || (mCameraState == PREVIEW_STOPPED)) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001537 mVolumeButtonClickedFlag = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001538 return;
1539 }
Michael Kolb8872c232013-01-29 10:33:22 -08001540
1541 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001542 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001543 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001544 + mActivity.getStorageSpaceBytes());
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001545 mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001546 return;
1547 }
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001548 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1549 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
Michael Kolb8872c232013-01-29 10:33:22 -08001550
Erin Dahlgren6190c362014-06-13 14:12:08 -07001551 int countDownDuration = mActivity.getSettingsManager()
1552 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
Andy Huibers547d7c82014-05-20 23:03:18 -07001553 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001554 if (countDownDuration > 0) {
1555 // Start count down.
1556 mAppController.getCameraAppUI().transitionToCancel();
1557 mAppController.getCameraAppUI().hideModeOptions();
1558 mUI.startCountdown(countDownDuration);
1559 return;
1560 } else {
1561 focusAndCapture();
1562 }
1563 }
1564
1565 private void focusAndCapture() {
Angus Kong6607dae2014-06-10 16:07:45 -07001566 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001567 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001568 }
Michael Kolb8872c232013-01-29 10:33:22 -08001569 // If the user wants to do a snapshot while the previous one is still
1570 // in progress, remember the fact and do it after we finish the previous
1571 // one and re-start the preview. Snapshot in progress also includes the
1572 // state that autofocus is focusing and a picture will be taken when
1573 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001574 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1575 if (!mIsImageCaptureIntent) {
1576 mSnapshotOnIdle = true;
1577 }
Michael Kolb8872c232013-01-29 10:33:22 -08001578 return;
1579 }
1580
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001581 mSnapshotOnIdle = false;
Angus Kong831347d2014-06-16 16:07:28 -07001582 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001583 }
1584
Doris Liu6c751642014-05-05 18:43:26 -07001585 @Override
1586 public void onRemainingSecondsChanged(int remainingSeconds) {
Andy Huibersa31162c2014-09-02 11:27:11 -07001587 if (remainingSeconds == 1) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001588 mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001589 } else if (remainingSeconds == 2 || remainingSeconds == 3) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001590 mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001591 }
Doris Liu6c751642014-05-05 18:43:26 -07001592 }
1593
1594 @Override
1595 public void onCountDownFinished() {
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001596 if (mIsImageCaptureIntent) {
1597 mAppController.getCameraAppUI().transitionToIntentReviewLayout();
1598 } else {
1599 mAppController.getCameraAppUI().transitionToCapture();
1600 }
Doris Liu6c751642014-05-05 18:43:26 -07001601 mAppController.getCameraAppUI().showModeOptions();
1602 if (mPaused) {
1603 return;
1604 }
1605 focusAndCapture();
1606 }
1607
Andy Huibersdef975d2013-11-22 09:13:39 -08001608 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001609 if (mPaused) {
1610 return;
1611 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001612 Log.v(TAG, "Executing onResumeTasks.");
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001613
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001614 mCountdownSoundPlayer.loadSound(R.raw.timer_final_second);
1615 mCountdownSoundPlayer.loadSound(R.raw.timer_increment);
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001616 if (mFocusManager != null) {
1617 // If camera is not open when resume is called, focus manager will
1618 // not be initialized yet, in which case it will start listening to
1619 // preview area size change later in the initialization.
1620 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1621 }
1622 mAppController.addPreviewAreaSizeChangedListener(mUI);
1623
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001624 CameraProvider camProvider = mActivity.getCameraProvider();
1625 if (camProvider == null) {
1626 // No camera provider, the Activity is destroyed already.
1627 return;
1628 }
Sol Boucher44ce4b22014-08-04 23:41:38 -07001629 requestCameraOpen();
Angus Kong20fad242013-11-11 18:23:46 -08001630
Michael Kolb8872c232013-01-29 10:33:22 -08001631 mJpegPictureCallbackTime = 0;
Sol Boucher2192fba2014-08-19 17:24:07 -07001632 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001633
1634 mOnResumeTime = SystemClock.uptimeMillis();
1635 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001636
1637 // If first time initialization is not finished, put it in the
1638 // message queue.
1639 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001640 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001641 } else {
1642 initializeSecondTime();
1643 }
Michael Kolb8872c232013-01-29 10:33:22 -08001644
Angus Kong0d00a892013-03-26 11:40:40 -07001645 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1646 if (gsensor != null) {
1647 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1648 }
1649
1650 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1651 if (msensor != null) {
1652 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1653 }
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001654
1655 getServices().getRemoteShutterListener().onModuleReady(this);
1656 SessionStatsCollector.instance().sessionActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001657 }
1658
Angus Kongc4e66562013-11-22 23:03:21 -08001659 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001660 * @return Whether the currently active camera is front-facing.
1661 */
1662 private boolean isCameraFrontFacing() {
Sol Boucher43e18132014-06-19 15:03:18 -07001663 return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1664 .isFacingFront();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001665 }
1666
1667 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001668 * The focus manager is the first UI related element to get initialized, and
1669 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001670 */
1671 private void initializeFocusManager() {
1672 // Create FocusManager object. startPreview needs it.
1673 // if mFocusManager not null, reuse it
1674 // otherwise create a new instance
1675 if (mFocusManager != null) {
1676 mFocusManager.removeMessages();
1677 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001678 mMirror = isCameraFrontFacing();
Angus Kong831347d2014-06-16 16:07:28 -07001679 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
Angus Kongc4e66562013-11-22 23:03:21 -08001680 R.array.pref_camera_focusmode_default_array);
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -07001681 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1682 new ArrayList<CameraCapabilities.FocusMode>();
Angus Kong831347d2014-06-16 16:07:28 -07001683 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1684 for (String modeString : defaultFocusModesStrings) {
1685 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1686 if (mode != null) {
1687 defaultFocusModes.add(mode);
1688 }
1689 }
1690 mFocusManager =
1691 new FocusOverlayManager(mAppController, defaultFocusModes,
1692 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
1693 mUI.getFocusUI());
Kevin Gabayanfb333362014-06-02 14:48:20 -07001694 MotionManager motionManager = getServices().getMotionManager();
1695 if (motionManager != null) {
1696 motionManager.addListener(mFocusManager);
1697 }
Angus Kongc4e66562013-11-22 23:03:21 -08001698 }
Doris Liu482de022013-12-18 19:18:16 -08001699 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001700 }
1701
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001702 /**
1703 * @return Whether we are resuming from within the lockscreen.
1704 */
1705 private boolean isResumeFromLockscreen() {
1706 String action = mActivity.getIntent().getAction();
1707 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1708 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1709 }
1710
Angus Kong20fad242013-11-11 18:23:46 -08001711 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001712 public void resume() {
1713 mPaused = false;
Doris Liu59401042014-01-14 17:51:32 -08001714
Angus Kongc4e66562013-11-22 23:03:21 -08001715 // Add delay on resume from lock screen only, in order to to speed up
1716 // the onResume --> onPause --> onResume cycle from lock screen.
1717 // Don't do always because letting go of thread can cause delay.
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001718 if (isResumeFromLockscreen()) {
Angus Kongc4e66562013-11-22 23:03:21 -08001719 Log.v(TAG, "On resume, from lock screen.");
1720 // Note: onPauseAfterSuper() will delete this runnable, so we will
1721 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001722 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001723 } else {
1724 Log.v(TAG, "On resume.");
1725 onResumeTasks();
1726 }
1727 }
1728
1729 @Override
1730 public void pause() {
Michael Kolb8872c232013-01-29 10:33:22 -08001731 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001732 mHandler.removeCallbacks(mResumeTaskRunnable);
Sascha Haeberling1bf08892014-06-18 09:38:06 -07001733 getServices().getRemoteShutterListener().onModuleExit();
Andy Huibers10c58162014-03-29 14:06:54 -07001734 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001735
Angus Kong0d00a892013-03-26 11:40:40 -07001736 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1737 if (gsensor != null) {
1738 mSensorManager.unregisterListener(this, gsensor);
1739 }
1740
1741 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1742 if (msensor != null) {
1743 mSensorManager.unregisterListener(this, msensor);
1744 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001745
Michael Kolb8872c232013-01-29 10:33:22 -08001746 // Reset the focus first. Camera CTS does not guarantee that
1747 // cancelAutoFocus is allowed after preview stops.
1748 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1749 mCameraDevice.cancelAutoFocus();
1750 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001751
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001752 // If the camera has not been opened asynchronously yet,
1753 // and startPreview hasn't been called, then this is a no-op.
1754 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001755 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001756 cancelCountDown();
1757 mCountdownSoundPlayer.release();
Michael Kolb8872c232013-01-29 10:33:22 -08001758
Angus Kongce5480e2013-01-29 17:43:48 -08001759 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001760 // If we are in an image capture intent and has taken
1761 // a picture, we just clear it in onPause.
1762 mJpegImageData = null;
1763
Angus Kongdcccc512013-08-08 17:06:03 -07001764 // Remove the messages and runnables in the queue.
1765 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001766
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001767 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001768 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001769 mUI.onPause();
1770
Michael Kolb8872c232013-01-29 10:33:22 -08001771 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001772 if (mFocusManager != null) {
1773 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001774 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001775 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001776 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001777 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001778
1779 SettingsManager settingsManager = mActivity.getSettingsManager();
1780 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001781 }
1782
Angus Kong20fad242013-11-11 18:23:46 -08001783 @Override
1784 public void destroy() {
1785 // TODO: implement this.
1786 }
1787
1788 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001789 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001790 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001791 }
1792
1793 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001794 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001795 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001796 setDisplayOrientation();
1797 }
1798 }
1799
Michael Kolb8872c232013-01-29 10:33:22 -08001800 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001801 return isCameraIdle()
1802 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001803 }
1804
1805 @Override
1806 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001807 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001808 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001809 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001810 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001811 setCameraState(FOCUSING);
1812 }
1813
1814 @Override
1815 public void cancelAutoFocus() {
1816 mCameraDevice.cancelAutoFocus();
1817 setCameraState(IDLE);
1818 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1819 }
1820
Michael Kolb8872c232013-01-29 10:33:22 -08001821 @Override
1822 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001823 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1824 || mCameraState == SNAPSHOT_IN_PROGRESS
1825 || mCameraState == SWITCHING_CAMERA
1826 || mCameraState == PREVIEW_STOPPED) {
1827 return;
1828 }
1829
1830 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001831 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1832 return;
1833 }
Doris Liu482de022013-12-18 19:18:16 -08001834 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001835 }
1836
1837 @Override
1838 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001839 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001840 }
1841
1842 @Override
1843 public boolean onKeyDown(int keyCode, KeyEvent event) {
1844 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001845 case KeyEvent.KEYCODE_VOLUME_UP:
1846 case KeyEvent.KEYCODE_VOLUME_DOWN:
1847 case KeyEvent.KEYCODE_FOCUS:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001848 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1849 !mActivity.getCameraAppUI().isInIntentReview()) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001850 if (event.getRepeatCount() == 0) {
1851 onShutterButtonFocus(true);
1852 }
1853 return true;
1854 }
1855 return false;
1856 case KeyEvent.KEYCODE_CAMERA:
1857 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1858 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001859 }
1860 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001861 case KeyEvent.KEYCODE_DPAD_CENTER:
1862 // If we get a dpad center event without any focused view, move
1863 // the focus to the shutter button and press it.
1864 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1865 // Start auto-focus immediately to reduce shutter lag. After
1866 // the shutter button gets the focus, onShutterButtonFocus()
1867 // will be called again but it is fine.
1868 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001869 }
1870 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001871 }
1872 return false;
1873 }
1874
1875 @Override
1876 public boolean onKeyUp(int keyCode, KeyEvent event) {
1877 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001878 case KeyEvent.KEYCODE_VOLUME_UP:
1879 case KeyEvent.KEYCODE_VOLUME_DOWN:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001880 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1881 !mActivity.getCameraAppUI().isInIntentReview()) {
1882 if (mUI.isCountingDown()) {
1883 cancelCountDown();
1884 } else {
1885 mVolumeButtonClickedFlag = true;
1886 onShutterButtonClick();
1887 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001888 return true;
1889 }
1890 return false;
1891 case KeyEvent.KEYCODE_FOCUS:
1892 if (mFirstTimeInitialized) {
1893 onShutterButtonFocus(false);
1894 }
Michael Kolb8872c232013-01-29 10:33:22 -08001895 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001896 }
1897 return false;
1898 }
1899
Michael Kolb8872c232013-01-29 10:33:22 -08001900 private void closeCamera() {
1901 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001902 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001903 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001904 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001905 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001906
Michael Kolb8872c232013-01-29 10:33:22 -08001907 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001908 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001909 mCameraDevice = null;
1910 setCameraState(PREVIEW_STOPPED);
1911 mFocusManager.onCameraReleased();
1912 }
1913 }
1914
1915 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001916 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
Sol Boucher43e18132014-06-19 15:03:18 -07001917 Characteristics info =
1918 mActivity.getCameraProvider().getCharacteristics(mCameraId);
Sol Boucher73ec9852014-07-24 23:59:21 -07001919 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001920 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001921 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001922 if (mFocusManager != null) {
1923 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1924 }
Doris Liu6432cd62013-06-13 17:20:31 -07001925 // Change the camera display orientation
1926 if (mCameraDevice != null) {
Sol Boucher73ec9852014-07-24 23:59:21 -07001927 mCameraDevice.setDisplayOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001928 }
Michael Kolb8872c232013-01-29 10:33:22 -08001929 }
1930
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001931 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001932 private void setupPreview() {
1933 mFocusManager.resetTouchFocus();
1934 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001935 }
1936
Angus Kong20fad242013-11-11 18:23:46 -08001937 /**
1938 * Returns whether we can/should start the preview or not.
1939 */
1940 private boolean checkPreviewPreconditions() {
1941 if (mPaused) {
1942 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001943 }
Michael Kolb8872c232013-01-29 10:33:22 -08001944
Angus Kong20fad242013-11-11 18:23:46 -08001945 if (mCameraDevice == null) {
1946 Log.w(TAG, "startPreview: camera device not ready yet.");
1947 return false;
1948 }
1949
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001950 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001951 if (st == null) {
1952 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001953 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001954 }
1955
1956 if (!mCameraPreviewParamsReady) {
1957 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001958 return false;
1959 }
1960 return true;
1961 }
1962
1963 /**
1964 * The start/stop preview should only run on the UI thread.
1965 */
1966 private void startPreview() {
Puneet Lall70a96522014-09-11 17:33:21 -07001967 // HACK: The call to setCameraParameters(UPDATE_PARAM_ALL) may
1968 // eventually recurse back into startPreview().
1969 // To avoid calling startPreview() twice, we must acquire
1970 // mStartPreviewLock.
1971 if (mStartPreviewLock) {
1972 // do nothing
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001973 return;
1974 }
Puneet Lall70a96522014-09-11 17:33:21 -07001975 mStartPreviewLock = true;
1976 try {
1977 if (!checkPreviewPreconditions()) {
1978 return;
Michael Kolb8872c232013-01-29 10:33:22 -08001979 }
Michael Kolb8872c232013-01-29 10:33:22 -08001980
Puneet Lall70a96522014-09-11 17:33:21 -07001981 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
1982 setDisplayOrientation();
Angus Kong20fad242013-11-11 18:23:46 -08001983
Puneet Lall70a96522014-09-11 17:33:21 -07001984 if (!mSnapshotOnIdle) {
1985 // If the focus mode is continuous autofocus, call cancelAutoFocus
1986 // to resume it because it may have been paused by autoFocus call.
1987 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1988 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
1989 mCameraDevice.cancelAutoFocus();
1990 }
1991 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1992 }
1993 setCameraParameters(UPDATE_PARAM_ALL);
1994 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
1995
1996 Log.i(TAG, "startPreview");
Spike Sprague963775c2014-09-18 19:44:07 -07001997 // If we're using API2 in portability layers, don't use startPreviewWithCallback()
1998 // b/17576554
1999 CameraAgent.CameraStartPreviewCallback startPreviewCallback =
2000 new CameraAgent.CameraStartPreviewCallback() {
2001 @Override
2002 public void onPreviewStarted() {
2003 mFocusManager.onPreviewStarted();
2004 PhotoModule.this.onPreviewStarted();
2005 SessionStatsCollector.instance().previewActive(true);
2006 if (mSnapshotOnIdle) {
2007 mHandler.post(mDoSnapRunnable);
2008 }
Spike Sprague3c3b31d2014-09-08 10:43:04 -07002009 }
Spike Sprague963775c2014-09-18 19:44:07 -07002010 };
2011 if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)) {
2012 mCameraDevice.startPreview();
2013 startPreviewCallback.onPreviewStarted();
2014 } else {
2015 mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
2016 startPreviewCallback);
2017 }
Puneet Lall70a96522014-09-11 17:33:21 -07002018 } finally {
2019 mStartPreviewLock = false;
Michael Kolb8872c232013-01-29 10:33:22 -08002020 }
2021 }
2022
Michael Kolbd6954f32013-03-08 20:43:01 -08002023 @Override
2024 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08002025 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07002026 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08002027 mCameraDevice.stopPreview();
2028 mFaceDetectionStarted = false;
2029 }
2030 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002031 if (mFocusManager != null) {
2032 mFocusManager.onPreviewStopped();
2033 }
Andy Huibers10c58162014-03-29 14:06:54 -07002034 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002035 }
2036
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002037 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -07002038 public void onSettingChanged(SettingsManager settingsManager, String key) {
2039 if (key.equals(Keys.KEY_FLASH_MODE)) {
2040 updateParametersFlashMode();
2041 }
2042 if (key.equals(Keys.KEY_CAMERA_HDR)) {
2043 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2044 Keys.KEY_CAMERA_HDR)) {
2045 // HDR is on.
2046 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
2047 mFlashModeBeforeSceneMode = settingsManager.getString(
2048 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
2049 } else {
2050 if (mFlashModeBeforeSceneMode != null) {
2051 settingsManager.set(mAppController.getCameraScope(),
2052 Keys.KEY_FLASH_MODE,
2053 mFlashModeBeforeSceneMode);
2054 updateParametersFlashMode();
2055 mFlashModeBeforeSceneMode = null;
Angus Kong8c07fe92014-05-22 14:42:45 -07002056 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07002057 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002058 }
2059 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08002060
2061 if (mCameraDevice != null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002062 mCameraDevice.applySettings(mCameraSettings);
Erin Dahlgren1648c362014-01-06 15:06:04 -08002063 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002064 }
2065
Michael Kolb8872c232013-01-29 10:33:22 -08002066 private void updateCameraParametersInitialize() {
2067 // Reset preview frame rate to the maximum because it may be lowered by
2068 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07002069 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07002070 if (fpsRange != null && fpsRange.length > 0) {
Angus Kong831347d2014-06-16 16:07:28 -07002071 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
Michael Kolb8872c232013-01-29 10:33:22 -08002072 }
2073
Angus Kong831347d2014-06-16 16:07:28 -07002074 mCameraSettings.setRecordingHintEnabled(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002075
Angus Kong6607dae2014-06-10 16:07:45 -07002076 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
2077 mCameraSettings.setVideoStabilization(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002078 }
2079 }
2080
2081 private void updateCameraParametersZoom() {
2082 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07002083 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002084 mCameraSettings.setZoomRatio(mZoomValue);
Michael Kolb8872c232013-01-29 10:33:22 -08002085 }
2086 }
2087
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002088 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002089 private void setAutoExposureLockIfSupported() {
2090 if (mAeLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002091 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002092 }
2093 }
2094
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002095 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002096 private void setAutoWhiteBalanceLockIfSupported() {
2097 if (mAwbLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002098 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002099 }
2100 }
2101
Michael Kolb8872c232013-01-29 10:33:22 -08002102 private void setFocusAreasIfSupported() {
2103 if (mFocusAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002104 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002105 }
2106 }
2107
Michael Kolb8872c232013-01-29 10:33:22 -08002108 private void setMeteringAreasIfSupported() {
2109 if (mMeteringAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002110 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002111 }
2112 }
2113
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002114 private void updateCameraParametersPreference() {
Spike Spragueae56c122014-09-19 11:18:46 -07002115 // some monkey tests can get here when shutting the app down
2116 // make sure mCameraDevice is still valid, b/17580046
2117 if (mCameraDevice == null) {
2118 return;
2119 }
2120
Michael Kolb8872c232013-01-29 10:33:22 -08002121 setAutoExposureLockIfSupported();
2122 setAutoWhiteBalanceLockIfSupported();
2123 setFocusAreasIfSupported();
2124 setMeteringAreasIfSupported();
2125
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002126 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07002127 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002128 mCameraSettings
2129 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Andy Huibers10c58162014-03-29 14:06:54 -07002130 SessionStatsCollector.instance().autofocusActive(
Angus Kong831347d2014-06-16 16:07:28 -07002131 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
2132 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
2133 );
Michael Kolbd3253f22013-07-12 11:36:47 -07002134
Michael Kolb8872c232013-01-29 10:33:22 -08002135 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002136 updateParametersPictureSize();
2137
2138 // Set JPEG quality.
2139 updateParametersPictureQuality();
2140
2141 // For the following settings, we need to check if the settings are
2142 // still supported by latest driver, if not, ignore the settings.
2143
2144 // Set exposure compensation
2145 updateParametersExposureCompensation();
2146
2147 // Set the scene mode: also sets flash and white balance.
2148 updateParametersSceneMode();
2149
2150 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2151 updateAutoFocusMoveCallback();
2152 }
2153 }
2154
2155 private void updateParametersPictureSize() {
2156 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002157 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT
2158 : Keys.KEY_PICTURE_SIZE_BACK;
2159 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
2160 pictureSizeKey);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002161
Angus Kong6607dae2014-06-10 16:07:45 -07002162 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Andy Huibers90c7ad52014-05-20 22:13:54 -07002163 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2164 mCameraDevice.getCameraId(), supported);
Angus Kong6607dae2014-06-10 16:07:45 -07002165 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002166 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002167
2168 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2169 mCameraDevice.getCameraId());
2170 if (ApiHelper.IS_NEXUS_5) {
2171 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2172 mShouldResizeTo16x9 = true;
2173 } else {
2174 mShouldResizeTo16x9 = false;
2175 }
2176 }
Michael Kolb8872c232013-01-29 10:33:22 -08002177
2178 // Set a preview size that is closest to the viewfinder height and has
2179 // the right aspect ratio.
Angus Kong6607dae2014-06-10 16:07:45 -07002180 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07002181 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002182 (double) size.width() / size.height());
Angus Kong6607dae2014-06-10 16:07:45 -07002183 Size original = mCameraSettings.getCurrentPreviewSize();
2184 if (!optimalSize.equals(original)) {
Alan Newberger2631a152014-09-24 14:23:30 -07002185 Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original);
Angus Kong6607dae2014-06-10 16:07:45 -07002186 mCameraSettings.setPreviewSize(optimalSize);
Doris Liu6432cd62013-06-13 17:20:31 -07002187
Michael Kolb8872c232013-01-29 10:33:22 -08002188 // Zoom related settings will be changed for different preview
2189 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002190 if (mHandler.getLooper() == Looper.myLooper()) {
Alan Newberger2631a152014-09-24 14:23:30 -07002191 Log.v(TAG, "matched looper, setting up preview");
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002192 // On UI thread only, not when camera starts up
2193 setupPreview();
2194 } else {
Alan Newberger2631a152014-09-24 14:23:30 -07002195 Log.v(TAG, "no looper match, directly applying settings");
Angus Kong6607dae2014-06-10 16:07:45 -07002196 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002197 }
Angus Kong6607dae2014-06-10 16:07:45 -07002198 mCameraSettings = mCameraDevice.getSettings();
Michael Kolb8872c232013-01-29 10:33:22 -08002199 }
Doris Liu95405742013-11-05 15:25:26 -08002200
Angus Kong00b7b102014-04-24 15:46:52 -07002201 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
Alan Newberger73514152014-09-10 15:03:27 -07002202 Log.v(TAG, "updating aspect ratio");
Angus Kong00b7b102014-04-24 15:46:52 -07002203 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2204 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002205 }
Alan Newberger73514152014-09-10 15:03:27 -07002206 Log.d(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002207 }
Michael Kolb8872c232013-01-29 10:33:22 -08002208
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002209 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002210 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2211 CameraProfile.QUALITY_HIGH);
Angus Kong6607dae2014-06-10 16:07:45 -07002212 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002213 }
Michael Kolb8872c232013-01-29 10:33:22 -08002214
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002215 private void updateParametersExposureCompensation() {
2216 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002217 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2218 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2219 int value = settingsManager.getInteger(mAppController.getCameraScope(),
2220 Keys.KEY_EXPOSURE);
Angus Kong88289042014-04-22 16:39:42 -07002221 int max = mCameraCapabilities.getMaxExposureCompensation();
2222 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002223 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002224 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002225 } else {
2226 Log.w(TAG, "invalid exposure range: " + value);
2227 }
Michael Kolb8872c232013-01-29 10:33:22 -08002228 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002229 // If exposure compensation is not enabled, reset the exposure compensation value.
2230 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002231 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002232
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002233 }
2234
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002235 private void updateParametersSceneMode() {
Angus Kong6607dae2014-06-10 16:07:45 -07002236 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002237 SettingsManager settingsManager = mActivity.getSettingsManager();
2238
Erin Dahlgren6190c362014-06-13 14:12:08 -07002239 mSceneMode = stringifier.
2240 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2241 Keys.KEY_SCENE_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002242 if (mCameraCapabilities.supports(mSceneMode)) {
2243 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2244 mCameraSettings.setSceneMode(mSceneMode);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002245
2246 // Setting scene mode will change the settings of flash mode,
2247 // white balance, and focus mode. Here we read back the
2248 // parameters, so we can know those settings.
Angus Kong6607dae2014-06-10 16:07:45 -07002249 mCameraDevice.applySettings(mCameraSettings);
2250 mCameraSettings = mCameraDevice.getSettings();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002251 }
2252 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002253 mSceneMode = mCameraSettings.getCurrentSceneMode();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002254 if (mSceneMode == null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002255 mSceneMode = CameraCapabilities.SceneMode.AUTO;
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002256 }
2257 }
2258
Angus Kong6607dae2014-06-10 16:07:45 -07002259 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
Michael Kolb8872c232013-01-29 10:33:22 -08002260 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002261 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002262
Michael Kolb8872c232013-01-29 10:33:22 -08002263 // Set focus mode.
2264 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002265 mCameraSettings.setFocusMode(
2266 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002267 } else {
Angus Kong831347d2014-06-16 16:07:28 -07002268 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08002269 }
Michael Kolb8872c232013-01-29 10:33:22 -08002270 }
2271
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002272 private void updateParametersFlashMode() {
2273 SettingsManager settingsManager = mActivity.getSettingsManager();
2274
Angus Kong6607dae2014-06-10 16:07:45 -07002275 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
Erin Dahlgren6190c362014-06-13 14:12:08 -07002276 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2277 Keys.KEY_FLASH_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002278 if (mCameraCapabilities.supports(flashMode)) {
2279 mCameraSettings.setFlashMode(flashMode);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002280 }
2281 }
2282
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002283 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002284 private void updateAutoFocusMoveCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -07002285 if (mCameraSettings.getCurrentFocusMode() ==
2286 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Angus Kong9ef99252013-07-18 18:04:19 -07002287 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002288 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002289 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002290 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002291 }
2292 }
2293
Spike Spragueabf54e22014-03-27 15:41:28 -07002294 /**
2295 * Sets the exposure compensation to the given value and also updates settings.
2296 *
2297 * @param value exposure compensation value to be set
2298 */
2299 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002300 int max = mCameraCapabilities.getMaxExposureCompensation();
2301 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002302 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002303 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002304 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002305 settingsManager.set(mAppController.getCameraScope(),
2306 Keys.KEY_EXPOSURE, value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002307 } else {
2308 Log.w(TAG, "invalid exposure range: " + value);
2309 }
2310 }
2311
Michael Kolb8872c232013-01-29 10:33:22 -08002312 // We separate the parameters into several subsets, so we can update only
2313 // the subsets actually need updating. The PREFERENCE set needs extra
2314 // locking because the preference can be changed from GLThread as well.
2315 private void setCameraParameters(int updateSet) {
2316 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2317 updateCameraParametersInitialize();
2318 }
2319
2320 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2321 updateCameraParametersZoom();
2322 }
2323
2324 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002325 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002326 }
2327
Spike Sprague5cc48d62014-09-22 10:52:32 -07002328 // some monkey tests can get here when shutting the app down
2329 // make sure mCameraDevice is still valid, b/17604028
2330 if (mCameraDevice != null) {
2331 mCameraDevice.applySettings(mCameraSettings);
2332 }
Michael Kolb8872c232013-01-29 10:33:22 -08002333 }
2334
2335 // If the Camera is idle, update the parameters immediately, otherwise
2336 // accumulate them in mUpdateSet and update later.
2337 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2338 mUpdateSet |= additionalUpdateSet;
2339 if (mCameraDevice == null) {
2340 // We will update all the parameters when we open the device, so
2341 // we don't need to do anything now.
2342 mUpdateSet = 0;
2343 return;
2344 } else if (isCameraIdle()) {
2345 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002346 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002347 mUpdateSet = 0;
2348 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002349 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2350 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002351 }
2352 }
2353 }
2354
ztenghui7b265a62013-09-09 14:58:44 -07002355 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002356 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002357 return (mCameraState == IDLE) ||
2358 (mCameraState == PREVIEW_STOPPED) ||
2359 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002360 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002361 }
2362
ztenghui7b265a62013-09-09 14:58:44 -07002363 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002364 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002365 String action = mActivity.getIntent().getAction();
2366 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002367 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002368 }
2369
2370 private void setupCaptureParams() {
2371 Bundle myExtras = mActivity.getIntent().getExtras();
2372 if (myExtras != null) {
2373 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2374 mCropValue = myExtras.getString("crop");
2375 }
2376 }
2377
Michael Kolb8872c232013-01-29 10:33:22 -08002378 private void initializeCapabilities() {
Angus Kong88289042014-04-22 16:39:42 -07002379 mCameraCapabilities = mCameraDevice.getCapabilities();
2380 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2381 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2382 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2383 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kong6607dae2014-06-10 16:07:45 -07002384 mContinuousFocusSupported =
2385 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002386 }
2387
Michael Kolb8872c232013-01-29 10:33:22 -08002388 @Override
Sol Boucher2192fba2014-08-19 17:24:07 -07002389 public void onZoomChanged(float ratio) {
Michael Kolbd6954f32013-03-08 20:43:01 -08002390 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002391 if (mPaused) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002392 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002393 }
Sol Boucher2192fba2014-08-19 17:24:07 -07002394 mZoomValue = ratio;
Angus Kong6607dae2014-06-10 16:07:45 -07002395 if (mCameraSettings == null || mCameraDevice == null) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002396 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002397 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002398 // Set zoom parameters asynchronously
Sol Boucher2192fba2014-08-19 17:24:07 -07002399 mCameraSettings.setZoomRatio(mZoomValue);
Angus Kong6607dae2014-06-10 16:07:45 -07002400 mCameraDevice.applySettings(mCameraSettings);
Angus Kongce5480e2013-01-29 17:43:48 -08002401 }
2402
2403 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002404 public int getCameraState() {
2405 return mCameraState;
2406 }
2407
2408 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002409 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002410 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002411 }
Angus Kong86d36312013-01-31 18:22:44 -08002412
2413 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002414 public void onLowMemory() {
2415 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002416 }
Angus Kong0d00a892013-03-26 11:40:40 -07002417
2418 @Override
2419 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2420 }
2421
2422 @Override
2423 public void onSensorChanged(SensorEvent event) {
2424 int type = event.sensor.getType();
2425 float[] data;
2426 if (type == Sensor.TYPE_ACCELEROMETER) {
2427 data = mGData;
2428 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2429 data = mMData;
2430 } else {
2431 // we should not be here.
2432 return;
2433 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002434 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002435 data[i] = event.values[i];
2436 }
2437 float[] orientation = new float[3];
2438 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2439 SensorManager.getOrientation(mR, orientation);
2440 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2441 if (mHeading < 0) {
2442 mHeading += 360;
2443 }
Angus Kong0d00a892013-03-26 11:40:40 -07002444 }
Doris Liu6432cd62013-06-13 17:20:31 -07002445
Ruben Brunkd217ed02013-10-08 23:31:13 -07002446 // For debugging only.
2447 public void setDebugUri(Uri uri) {
2448 mDebugUri = uri;
2449 }
2450
2451 // For debugging only.
2452 private void saveToDebugUri(byte[] data) {
2453 if (mDebugUri != null) {
2454 OutputStream outputStream = null;
2455 try {
2456 outputStream = mContentResolver.openOutputStream(mDebugUri);
2457 outputStream.write(data);
2458 outputStream.close();
2459 } catch (IOException e) {
2460 Log.e(TAG, "Exception while writing debug jpeg file", e);
2461 } finally {
2462 CameraUtil.closeSilently(outputStream);
2463 }
2464 }
2465 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002466
2467 @Override
2468 public void onRemoteShutterPress() {
Sascha Haeberling57d4f742014-06-25 11:05:49 -07002469 mHandler.post(new Runnable() {
2470 @Override
2471 public void run() {
2472 focusAndCapture();
2473 }
2474 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002475 }
Spike Spragueeeeed4f2014-08-27 12:01:36 -07002476
2477 /**
2478 * This class manages the loading/releasing/playing of the sounds needed for
2479 * countdown timer.
2480 */
2481 private class CountdownSoundPlayer {
2482 private SoundPool mSoundPool;
2483 private int mTimerIncrement;
2484 private int mTimerFinalSecond;
2485
2486 void loadSounds() {
2487 // Load the sounds.
2488 if (mSoundPool == null) {
2489 mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2490 mTimerIncrement = mSoundPool.load(mAppController.getAndroidContext(), R.raw.timer_increment, 1);
2491 mTimerFinalSecond = mSoundPool.load(mAppController.getAndroidContext(), R.raw.timer_final_second, 1);
2492 }
2493 }
2494
2495 void onRemainingSecondsChanged(int newVal) {
2496 if (mSoundPool == null) {
2497 Log.e(TAG, "Cannot play sound - they have not been loaded.");
2498 return;
2499 }
2500 if (newVal == 1) {
2501 mSoundPool.play(mTimerFinalSecond, 1.0f, 1.0f, 0, 0, 1.0f);
2502 } else if (newVal == 2 || newVal == 3) {
2503 mSoundPool.play(mTimerIncrement, 1.0f, 1.0f, 0, 0, 1.0f);
2504 }
2505 }
2506
2507 void release() {
2508 if (mSoundPool != null) {
2509 mSoundPool.release();
2510 mSoundPool = null;
2511 }
2512 }
2513 }
Michael Kolb8872c232013-01-29 10:33:22 -08002514}