blob: 7afa9156e1b9f3e6710f78f4461c2bc1d06b3f4c [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();
Alan Newberger347dccb2014-09-24 17:19:25 -07001359 if (!CameraCapabilities.FlashMode.NO_FLASH.equals(flashMode)) {
1360 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FLASH_MODE,
1361 stringifier.stringify(flashMode));
1362 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07001363 settingsManager.set(mAppController.getCameraScope(), Keys.KEY_FOCUS_MODE,
Alan Newberger347dccb2014-09-24 17:19:25 -07001364 stringifier.stringify(focusMode));
Michael Kolb8872c232013-01-29 10:33:22 -08001365 }
1366
1367 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001368 public void onOrientationChanged(int orientation) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001369 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1370 return;
1371 }
Senpo Hubb1c72f2014-09-08 01:07:27 -07001372
1373 // TODO: Document orientation compute logic and unify them in OrientationManagerImpl.
1374 // b/17443789
1375 // Flip to counter-clockwise orientation.
1376 mOrientation = (360 - orientation) % 360;
Michael Kolb8872c232013-01-29 10:33:22 -08001377 }
1378
1379 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001380 public void onCameraAvailable(CameraProxy cameraProxy) {
Alan Newberger29a009c2014-09-05 12:47:42 -07001381 Log.v(TAG, "onCameraAvailable");
Angus Kong20fad242013-11-11 18:23:46 -08001382 if (mPaused) {
1383 return;
1384 }
1385 mCameraDevice = cameraProxy;
1386
1387 initializeCapabilities();
1388
1389 // Reset zoom value index.
Sol Boucher2192fba2014-08-19 17:24:07 -07001390 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001391 if (mFocusManager == null) {
1392 initializeFocusManager();
1393 }
Angus Kong831347d2014-06-16 16:07:28 -07001394 mFocusManager.updateCapabilities(mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001395
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001396 // Do camera parameter dependent initialization.
Angus Kong6607dae2014-06-10 16:07:45 -07001397 mCameraSettings = mCameraDevice.getSettings();
Puneet Lall70a96522014-09-11 17:33:21 -07001398 // HACK: The call to setCameraParameters(UPDATE_PARAM_ALL) may
1399 // eventually recurse back into startPreview().
1400 // To avoid calling startPreview() twice, first acquire
1401 // mStartPreviewLock.
1402 mStartPreviewLock = true;
1403 try {
1404 setCameraParameters(UPDATE_PARAM_ALL);
1405 // Set a listener which updates camera parameters based
1406 // on changed settings.
1407 SettingsManager settingsManager = mActivity.getSettingsManager();
1408 settingsManager.addListener(this);
1409 mCameraPreviewParamsReady = true;
1410 } finally {
1411 mStartPreviewLock = false;
1412 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001413
Angus Kong20fad242013-11-11 18:23:46 -08001414 startPreview();
1415
1416 onCameraOpened();
1417 }
1418
1419 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001420 public void onCaptureCancelled() {
1421 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1422 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001423 }
1424
Michael Kolbd6954f32013-03-08 20:43:01 -08001425 @Override
1426 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001427 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001428 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001429 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001430 mUI.hidePostCaptureAlert();
Spike Spragueb4a22222014-05-22 14:40:53 -07001431 mUI.hideIntentReviewImageView();
Michael Kolb8872c232013-01-29 10:33:22 -08001432 setupPreview();
1433 }
1434
Michael Kolbd6954f32013-03-08 20:43:01 -08001435 @Override
1436 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001437 if (mPaused) {
1438 return;
1439 }
1440
1441 byte[] data = mJpegImageData;
1442
1443 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001444 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001445 // caller specifies a "save uri" then write the data to its
1446 // stream. Otherwise, pass back a scaled down version of the bitmap
1447 // directly in the extras.
1448 if (mSaveUri != null) {
1449 OutputStream outputStream = null;
1450 try {
1451 outputStream = mContentResolver.openOutputStream(mSaveUri);
1452 outputStream.write(data);
1453 outputStream.close();
1454
Alan Newbergerf974ba72014-09-08 14:44:30 -07001455 Log.v(TAG, "saved result to URI: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001456 mActivity.setResultEx(Activity.RESULT_OK);
1457 mActivity.finish();
1458 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001459 Log.w(TAG, "exception saving result to URI: " + mSaveUri, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001460 // ignore exception
1461 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001462 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001463 }
1464 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001465 ExifInterface exif = Exif.getExif(data);
1466 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001467 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1468 bitmap = CameraUtil.rotate(bitmap, orientation);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001469 Log.v(TAG, "inlined bitmap into capture intent result");
Michael Kolb8872c232013-01-29 10:33:22 -08001470 mActivity.setResultEx(Activity.RESULT_OK,
1471 new Intent("inline-data").putExtra("data", bitmap));
1472 mActivity.finish();
1473 }
1474 } else {
1475 // Save the image to a temp file and invoke the cropper
1476 Uri tempUri = null;
1477 FileOutputStream tempStream = null;
1478 try {
1479 File path = mActivity.getFileStreamPath(sTempCropFilename);
1480 path.delete();
1481 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1482 tempStream.write(data);
1483 tempStream.close();
1484 tempUri = Uri.fromFile(path);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001485 Log.v(TAG, "wrote temp file for cropping to: " + sTempCropFilename);
Michael Kolb8872c232013-01-29 10:33:22 -08001486 } catch (FileNotFoundException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001487 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001488 mActivity.setResultEx(Activity.RESULT_CANCELED);
1489 mActivity.finish();
1490 return;
1491 } catch (IOException ex) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001492 Log.w(TAG, "error writing temp cropping file to: " + sTempCropFilename, ex);
Michael Kolb8872c232013-01-29 10:33:22 -08001493 mActivity.setResultEx(Activity.RESULT_CANCELED);
1494 mActivity.finish();
1495 return;
1496 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001497 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001498 }
1499
1500 Bundle newExtras = new Bundle();
1501 if (mCropValue.equals("circle")) {
1502 newExtras.putString("circleCrop", "true");
1503 }
1504 if (mSaveUri != null) {
Alan Newbergerf974ba72014-09-08 14:44:30 -07001505 Log.v(TAG, "setting output of cropped file to: " + mSaveUri);
Michael Kolb8872c232013-01-29 10:33:22 -08001506 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1507 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001508 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001509 }
1510 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001511 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001512 }
1513
Sascha Haeberling37f36112013-08-06 14:31:52 -07001514 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001515 final String CROP_ACTION = "com.android.camera.action.CROP";
1516 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001517
1518 cropIntent.setData(tempUri);
1519 cropIntent.putExtras(newExtras);
Alan Newbergerf974ba72014-09-08 14:44:30 -07001520 Log.v(TAG, "starting CROP intent for capture");
Michael Kolb8872c232013-01-29 10:33:22 -08001521 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1522 }
1523 }
1524
Michael Kolb8872c232013-01-29 10:33:22 -08001525 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001526 public void onShutterCoordinate(TouchCoordinate coord) {
1527 mShutterTouchCoordinate = coord;
1528 }
1529
1530 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001531 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001532 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001533 }
1534
1535 @Override
1536 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001537 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001538 || (mCameraState == PREVIEW_STOPPED)) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001539 mVolumeButtonClickedFlag = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001540 return;
1541 }
Michael Kolb8872c232013-01-29 10:33:22 -08001542
1543 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001544 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001545 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001546 + mActivity.getStorageSpaceBytes());
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001547 mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001548 return;
1549 }
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001550 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1551 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
Michael Kolb8872c232013-01-29 10:33:22 -08001552
Erin Dahlgren6190c362014-06-13 14:12:08 -07001553 int countDownDuration = mActivity.getSettingsManager()
1554 .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
Andy Huibers547d7c82014-05-20 23:03:18 -07001555 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001556 if (countDownDuration > 0) {
1557 // Start count down.
1558 mAppController.getCameraAppUI().transitionToCancel();
1559 mAppController.getCameraAppUI().hideModeOptions();
1560 mUI.startCountdown(countDownDuration);
1561 return;
1562 } else {
1563 focusAndCapture();
1564 }
1565 }
1566
1567 private void focusAndCapture() {
Angus Kong6607dae2014-06-10 16:07:45 -07001568 if (mSceneMode == CameraCapabilities.SceneMode.HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001569 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001570 }
Michael Kolb8872c232013-01-29 10:33:22 -08001571 // If the user wants to do a snapshot while the previous one is still
1572 // in progress, remember the fact and do it after we finish the previous
1573 // one and re-start the preview. Snapshot in progress also includes the
1574 // state that autofocus is focusing and a picture will be taken when
1575 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001576 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1577 if (!mIsImageCaptureIntent) {
1578 mSnapshotOnIdle = true;
1579 }
Michael Kolb8872c232013-01-29 10:33:22 -08001580 return;
1581 }
1582
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001583 mSnapshotOnIdle = false;
Angus Kong831347d2014-06-16 16:07:28 -07001584 mFocusManager.focusAndCapture(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001585 }
1586
Doris Liu6c751642014-05-05 18:43:26 -07001587 @Override
1588 public void onRemainingSecondsChanged(int remainingSeconds) {
Andy Huibersa31162c2014-09-02 11:27:11 -07001589 if (remainingSeconds == 1) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001590 mCountdownSoundPlayer.play(R.raw.timer_final_second, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001591 } else if (remainingSeconds == 2 || remainingSeconds == 3) {
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001592 mCountdownSoundPlayer.play(R.raw.timer_increment, 0.6f);
Andy Huibersa31162c2014-09-02 11:27:11 -07001593 }
Doris Liu6c751642014-05-05 18:43:26 -07001594 }
1595
1596 @Override
1597 public void onCountDownFinished() {
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001598 if (mIsImageCaptureIntent) {
1599 mAppController.getCameraAppUI().transitionToIntentReviewLayout();
1600 } else {
1601 mAppController.getCameraAppUI().transitionToCapture();
1602 }
Doris Liu6c751642014-05-05 18:43:26 -07001603 mAppController.getCameraAppUI().showModeOptions();
1604 if (mPaused) {
1605 return;
1606 }
1607 focusAndCapture();
1608 }
1609
Andy Huibersdef975d2013-11-22 09:13:39 -08001610 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001611 if (mPaused) {
1612 return;
1613 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001614 Log.v(TAG, "Executing onResumeTasks.");
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001615
Spike Spragueeeeed4f2014-08-27 12:01:36 -07001616 mCountdownSoundPlayer.loadSound(R.raw.timer_final_second);
1617 mCountdownSoundPlayer.loadSound(R.raw.timer_increment);
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001618 if (mFocusManager != null) {
1619 // If camera is not open when resume is called, focus manager will
1620 // not be initialized yet, in which case it will start listening to
1621 // preview area size change later in the initialization.
1622 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1623 }
1624 mAppController.addPreviewAreaSizeChangedListener(mUI);
1625
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001626 CameraProvider camProvider = mActivity.getCameraProvider();
1627 if (camProvider == null) {
1628 // No camera provider, the Activity is destroyed already.
1629 return;
1630 }
Sol Boucher44ce4b22014-08-04 23:41:38 -07001631 requestCameraOpen();
Angus Kong20fad242013-11-11 18:23:46 -08001632
Michael Kolb8872c232013-01-29 10:33:22 -08001633 mJpegPictureCallbackTime = 0;
Sol Boucher2192fba2014-08-19 17:24:07 -07001634 mZoomValue = 1.0f;
Angus Kong20fad242013-11-11 18:23:46 -08001635
1636 mOnResumeTime = SystemClock.uptimeMillis();
1637 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001638
1639 // If first time initialization is not finished, put it in the
1640 // message queue.
1641 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001642 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001643 } else {
1644 initializeSecondTime();
1645 }
Michael Kolb8872c232013-01-29 10:33:22 -08001646
Angus Kong0d00a892013-03-26 11:40:40 -07001647 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1648 if (gsensor != null) {
1649 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1650 }
1651
1652 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1653 if (msensor != null) {
1654 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1655 }
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001656
1657 getServices().getRemoteShutterListener().onModuleReady(this);
1658 SessionStatsCollector.instance().sessionActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001659 }
1660
Angus Kongc4e66562013-11-22 23:03:21 -08001661 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001662 * @return Whether the currently active camera is front-facing.
1663 */
1664 private boolean isCameraFrontFacing() {
Sol Boucher43e18132014-06-19 15:03:18 -07001665 return mAppController.getCameraProvider().getCharacteristics(mCameraId)
1666 .isFacingFront();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001667 }
1668
1669 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001670 * The focus manager is the first UI related element to get initialized, and
1671 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001672 */
1673 private void initializeFocusManager() {
1674 // Create FocusManager object. startPreview needs it.
1675 // if mFocusManager not null, reuse it
1676 // otherwise create a new instance
1677 if (mFocusManager != null) {
1678 mFocusManager.removeMessages();
1679 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001680 mMirror = isCameraFrontFacing();
Angus Kong831347d2014-06-16 16:07:28 -07001681 String[] defaultFocusModesStrings = mActivity.getResources().getStringArray(
Angus Kongc4e66562013-11-22 23:03:21 -08001682 R.array.pref_camera_focusmode_default_array);
Sascha Haeberlingee36c5f2014-07-29 17:05:43 -07001683 ArrayList<CameraCapabilities.FocusMode> defaultFocusModes =
1684 new ArrayList<CameraCapabilities.FocusMode>();
Angus Kong831347d2014-06-16 16:07:28 -07001685 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
1686 for (String modeString : defaultFocusModesStrings) {
1687 CameraCapabilities.FocusMode mode = stringifier.focusModeFromString(modeString);
1688 if (mode != null) {
1689 defaultFocusModes.add(mode);
1690 }
1691 }
1692 mFocusManager =
1693 new FocusOverlayManager(mAppController, defaultFocusModes,
1694 mCameraCapabilities, this, mMirror, mActivity.getMainLooper(),
1695 mUI.getFocusUI());
Kevin Gabayanfb333362014-06-02 14:48:20 -07001696 MotionManager motionManager = getServices().getMotionManager();
1697 if (motionManager != null) {
1698 motionManager.addListener(mFocusManager);
1699 }
Angus Kongc4e66562013-11-22 23:03:21 -08001700 }
Doris Liu482de022013-12-18 19:18:16 -08001701 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001702 }
1703
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001704 /**
1705 * @return Whether we are resuming from within the lockscreen.
1706 */
1707 private boolean isResumeFromLockscreen() {
1708 String action = mActivity.getIntent().getAction();
1709 return (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1710 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action));
1711 }
1712
Angus Kong20fad242013-11-11 18:23:46 -08001713 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001714 public void resume() {
1715 mPaused = false;
Doris Liu59401042014-01-14 17:51:32 -08001716
Angus Kongc4e66562013-11-22 23:03:21 -08001717 // Add delay on resume from lock screen only, in order to to speed up
1718 // the onResume --> onPause --> onResume cycle from lock screen.
1719 // Don't do always because letting go of thread can cause delay.
Sascha Haeberlingdcea7632014-07-17 14:06:02 -07001720 if (isResumeFromLockscreen()) {
Angus Kongc4e66562013-11-22 23:03:21 -08001721 Log.v(TAG, "On resume, from lock screen.");
1722 // Note: onPauseAfterSuper() will delete this runnable, so we will
1723 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001724 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001725 } else {
1726 Log.v(TAG, "On resume.");
1727 onResumeTasks();
1728 }
1729 }
1730
1731 @Override
1732 public void pause() {
Michael Kolb8872c232013-01-29 10:33:22 -08001733 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001734 mHandler.removeCallbacks(mResumeTaskRunnable);
Sascha Haeberling1bf08892014-06-18 09:38:06 -07001735 getServices().getRemoteShutterListener().onModuleExit();
Andy Huibers10c58162014-03-29 14:06:54 -07001736 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001737
Angus Kong0d00a892013-03-26 11:40:40 -07001738 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1739 if (gsensor != null) {
1740 mSensorManager.unregisterListener(this, gsensor);
1741 }
1742
1743 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1744 if (msensor != null) {
1745 mSensorManager.unregisterListener(this, msensor);
1746 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001747
Michael Kolb8872c232013-01-29 10:33:22 -08001748 // Reset the focus first. Camera CTS does not guarantee that
1749 // cancelAutoFocus is allowed after preview stops.
1750 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1751 mCameraDevice.cancelAutoFocus();
1752 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001753
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001754 // If the camera has not been opened asynchronously yet,
1755 // and startPreview hasn't been called, then this is a no-op.
1756 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001757 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001758 cancelCountDown();
1759 mCountdownSoundPlayer.release();
Michael Kolb8872c232013-01-29 10:33:22 -08001760
Angus Kongce5480e2013-01-29 17:43:48 -08001761 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001762 // If we are in an image capture intent and has taken
1763 // a picture, we just clear it in onPause.
1764 mJpegImageData = null;
1765
Angus Kongdcccc512013-08-08 17:06:03 -07001766 // Remove the messages and runnables in the queue.
1767 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001768
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001769 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001770 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001771 mUI.onPause();
1772
Michael Kolb8872c232013-01-29 10:33:22 -08001773 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001774 if (mFocusManager != null) {
1775 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001776 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001777 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001778 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001779 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001780
1781 SettingsManager settingsManager = mActivity.getSettingsManager();
1782 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001783 }
1784
Angus Kong20fad242013-11-11 18:23:46 -08001785 @Override
1786 public void destroy() {
1787 // TODO: implement this.
1788 }
1789
1790 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001791 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001792 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001793 }
1794
1795 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001796 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001797 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001798 setDisplayOrientation();
1799 }
1800 }
1801
Michael Kolb8872c232013-01-29 10:33:22 -08001802 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001803 return isCameraIdle()
1804 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001805 }
1806
1807 @Override
1808 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001809 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001810 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001811 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001812 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001813 setCameraState(FOCUSING);
1814 }
1815
1816 @Override
1817 public void cancelAutoFocus() {
1818 mCameraDevice.cancelAutoFocus();
1819 setCameraState(IDLE);
1820 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1821 }
1822
Michael Kolb8872c232013-01-29 10:33:22 -08001823 @Override
1824 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001825 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1826 || mCameraState == SNAPSHOT_IN_PROGRESS
1827 || mCameraState == SWITCHING_CAMERA
1828 || mCameraState == PREVIEW_STOPPED) {
1829 return;
1830 }
1831
1832 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001833 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1834 return;
1835 }
Doris Liu482de022013-12-18 19:18:16 -08001836 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001837 }
1838
1839 @Override
1840 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001841 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001842 }
1843
1844 @Override
1845 public boolean onKeyDown(int keyCode, KeyEvent event) {
1846 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001847 case KeyEvent.KEYCODE_VOLUME_UP:
1848 case KeyEvent.KEYCODE_VOLUME_DOWN:
1849 case KeyEvent.KEYCODE_FOCUS:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001850 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1851 !mActivity.getCameraAppUI().isInIntentReview()) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001852 if (event.getRepeatCount() == 0) {
1853 onShutterButtonFocus(true);
1854 }
1855 return true;
1856 }
1857 return false;
1858 case KeyEvent.KEYCODE_CAMERA:
1859 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1860 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001861 }
1862 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001863 case KeyEvent.KEYCODE_DPAD_CENTER:
1864 // If we get a dpad center event without any focused view, move
1865 // the focus to the shutter button and press it.
1866 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1867 // Start auto-focus immediately to reduce shutter lag. After
1868 // the shutter button gets the focus, onShutterButtonFocus()
1869 // will be called again but it is fine.
1870 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001871 }
1872 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001873 }
1874 return false;
1875 }
1876
1877 @Override
1878 public boolean onKeyUp(int keyCode, KeyEvent event) {
1879 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001880 case KeyEvent.KEYCODE_VOLUME_UP:
1881 case KeyEvent.KEYCODE_VOLUME_DOWN:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001882 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1883 !mActivity.getCameraAppUI().isInIntentReview()) {
1884 if (mUI.isCountingDown()) {
1885 cancelCountDown();
1886 } else {
1887 mVolumeButtonClickedFlag = true;
1888 onShutterButtonClick();
1889 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001890 return true;
1891 }
1892 return false;
1893 case KeyEvent.KEYCODE_FOCUS:
1894 if (mFirstTimeInitialized) {
1895 onShutterButtonFocus(false);
1896 }
Michael Kolb8872c232013-01-29 10:33:22 -08001897 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001898 }
1899 return false;
1900 }
1901
Michael Kolb8872c232013-01-29 10:33:22 -08001902 private void closeCamera() {
1903 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001904 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001905 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001906 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001907 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001908
Michael Kolb8872c232013-01-29 10:33:22 -08001909 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001910 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001911 mCameraDevice = null;
1912 setCameraState(PREVIEW_STOPPED);
1913 mFocusManager.onCameraReleased();
1914 }
1915 }
1916
1917 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001918 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
Sol Boucher43e18132014-06-19 15:03:18 -07001919 Characteristics info =
1920 mActivity.getCameraProvider().getCharacteristics(mCameraId);
Sol Boucher73ec9852014-07-24 23:59:21 -07001921 mDisplayOrientation = info.getPreviewOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001922 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001923 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001924 if (mFocusManager != null) {
1925 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1926 }
Doris Liu6432cd62013-06-13 17:20:31 -07001927 // Change the camera display orientation
1928 if (mCameraDevice != null) {
Sol Boucher73ec9852014-07-24 23:59:21 -07001929 mCameraDevice.setDisplayOrientation(mDisplayRotation);
Doris Liu6432cd62013-06-13 17:20:31 -07001930 }
Michael Kolb8872c232013-01-29 10:33:22 -08001931 }
1932
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001933 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001934 private void setupPreview() {
1935 mFocusManager.resetTouchFocus();
1936 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001937 }
1938
Angus Kong20fad242013-11-11 18:23:46 -08001939 /**
1940 * Returns whether we can/should start the preview or not.
1941 */
1942 private boolean checkPreviewPreconditions() {
1943 if (mPaused) {
1944 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001945 }
Michael Kolb8872c232013-01-29 10:33:22 -08001946
Angus Kong20fad242013-11-11 18:23:46 -08001947 if (mCameraDevice == null) {
1948 Log.w(TAG, "startPreview: camera device not ready yet.");
1949 return false;
1950 }
1951
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001952 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001953 if (st == null) {
1954 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001955 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001956 }
1957
1958 if (!mCameraPreviewParamsReady) {
1959 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001960 return false;
1961 }
1962 return true;
1963 }
1964
1965 /**
1966 * The start/stop preview should only run on the UI thread.
1967 */
1968 private void startPreview() {
Puneet Lall70a96522014-09-11 17:33:21 -07001969 // HACK: The call to setCameraParameters(UPDATE_PARAM_ALL) may
1970 // eventually recurse back into startPreview().
1971 // To avoid calling startPreview() twice, we must acquire
1972 // mStartPreviewLock.
1973 if (mStartPreviewLock) {
1974 // do nothing
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001975 return;
1976 }
Puneet Lall70a96522014-09-11 17:33:21 -07001977 mStartPreviewLock = true;
1978 try {
1979 if (!checkPreviewPreconditions()) {
1980 return;
Michael Kolb8872c232013-01-29 10:33:22 -08001981 }
Michael Kolb8872c232013-01-29 10:33:22 -08001982
Puneet Lall70a96522014-09-11 17:33:21 -07001983 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
1984 setDisplayOrientation();
Angus Kong20fad242013-11-11 18:23:46 -08001985
Puneet Lall70a96522014-09-11 17:33:21 -07001986 if (!mSnapshotOnIdle) {
1987 // If the focus mode is continuous autofocus, call cancelAutoFocus
1988 // to resume it because it may have been paused by autoFocus call.
1989 if (mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
1990 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
1991 mCameraDevice.cancelAutoFocus();
1992 }
1993 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1994 }
1995 setCameraParameters(UPDATE_PARAM_ALL);
1996 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
1997
1998 Log.i(TAG, "startPreview");
Spike Sprague963775c2014-09-18 19:44:07 -07001999 // If we're using API2 in portability layers, don't use startPreviewWithCallback()
2000 // b/17576554
2001 CameraAgent.CameraStartPreviewCallback startPreviewCallback =
2002 new CameraAgent.CameraStartPreviewCallback() {
2003 @Override
2004 public void onPreviewStarted() {
2005 mFocusManager.onPreviewStarted();
2006 PhotoModule.this.onPreviewStarted();
2007 SessionStatsCollector.instance().previewActive(true);
2008 if (mSnapshotOnIdle) {
2009 mHandler.post(mDoSnapRunnable);
2010 }
Spike Sprague3c3b31d2014-09-08 10:43:04 -07002011 }
Spike Sprague963775c2014-09-18 19:44:07 -07002012 };
2013 if (GservicesHelper.useCamera2ApiThroughPortabilityLayer(mActivity)) {
2014 mCameraDevice.startPreview();
2015 startPreviewCallback.onPreviewStarted();
2016 } else {
2017 mCameraDevice.startPreviewWithCallback(new Handler(Looper.getMainLooper()),
2018 startPreviewCallback);
2019 }
Puneet Lall70a96522014-09-11 17:33:21 -07002020 } finally {
2021 mStartPreviewLock = false;
Michael Kolb8872c232013-01-29 10:33:22 -08002022 }
2023 }
2024
Michael Kolbd6954f32013-03-08 20:43:01 -08002025 @Override
2026 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08002027 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07002028 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08002029 mCameraDevice.stopPreview();
2030 mFaceDetectionStarted = false;
2031 }
2032 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002033 if (mFocusManager != null) {
2034 mFocusManager.onPreviewStopped();
2035 }
Andy Huibers10c58162014-03-29 14:06:54 -07002036 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002037 }
2038
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002039 @Override
Erin Dahlgren6190c362014-06-13 14:12:08 -07002040 public void onSettingChanged(SettingsManager settingsManager, String key) {
2041 if (key.equals(Keys.KEY_FLASH_MODE)) {
2042 updateParametersFlashMode();
2043 }
2044 if (key.equals(Keys.KEY_CAMERA_HDR)) {
2045 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2046 Keys.KEY_CAMERA_HDR)) {
2047 // HDR is on.
2048 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
2049 mFlashModeBeforeSceneMode = settingsManager.getString(
2050 mAppController.getCameraScope(), Keys.KEY_FLASH_MODE);
2051 } else {
2052 if (mFlashModeBeforeSceneMode != null) {
2053 settingsManager.set(mAppController.getCameraScope(),
2054 Keys.KEY_FLASH_MODE,
2055 mFlashModeBeforeSceneMode);
2056 updateParametersFlashMode();
2057 mFlashModeBeforeSceneMode = null;
Angus Kong8c07fe92014-05-22 14:42:45 -07002058 }
Erin Dahlgren6190c362014-06-13 14:12:08 -07002059 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002060 }
2061 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08002062
2063 if (mCameraDevice != null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002064 mCameraDevice.applySettings(mCameraSettings);
Erin Dahlgren1648c362014-01-06 15:06:04 -08002065 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002066 }
2067
Michael Kolb8872c232013-01-29 10:33:22 -08002068 private void updateCameraParametersInitialize() {
2069 // Reset preview frame rate to the maximum because it may be lowered by
2070 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07002071 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07002072 if (fpsRange != null && fpsRange.length > 0) {
Angus Kong831347d2014-06-16 16:07:28 -07002073 mCameraSettings.setPreviewFpsRange(fpsRange[0], fpsRange[1]);
Michael Kolb8872c232013-01-29 10:33:22 -08002074 }
2075
Angus Kong831347d2014-06-16 16:07:28 -07002076 mCameraSettings.setRecordingHintEnabled(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002077
Angus Kong6607dae2014-06-10 16:07:45 -07002078 if (mCameraCapabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
2079 mCameraSettings.setVideoStabilization(false);
Michael Kolb8872c232013-01-29 10:33:22 -08002080 }
2081 }
2082
2083 private void updateCameraParametersZoom() {
2084 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07002085 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002086 mCameraSettings.setZoomRatio(mZoomValue);
Michael Kolb8872c232013-01-29 10:33:22 -08002087 }
2088 }
2089
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002090 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002091 private void setAutoExposureLockIfSupported() {
2092 if (mAeLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002093 mCameraSettings.setAutoExposureLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002094 }
2095 }
2096
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002097 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002098 private void setAutoWhiteBalanceLockIfSupported() {
2099 if (mAwbLockSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002100 mCameraSettings.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
Michael Kolb8872c232013-01-29 10:33:22 -08002101 }
2102 }
2103
Michael Kolb8872c232013-01-29 10:33:22 -08002104 private void setFocusAreasIfSupported() {
2105 if (mFocusAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002106 mCameraSettings.setFocusAreas(mFocusManager.getFocusAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002107 }
2108 }
2109
Michael Kolb8872c232013-01-29 10:33:22 -08002110 private void setMeteringAreasIfSupported() {
2111 if (mMeteringAreaSupported) {
Angus Kong6607dae2014-06-10 16:07:45 -07002112 mCameraSettings.setMeteringAreas(mFocusManager.getMeteringAreas());
Michael Kolb8872c232013-01-29 10:33:22 -08002113 }
2114 }
2115
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002116 private void updateCameraParametersPreference() {
Spike Spragueae56c122014-09-19 11:18:46 -07002117 // some monkey tests can get here when shutting the app down
2118 // make sure mCameraDevice is still valid, b/17580046
2119 if (mCameraDevice == null) {
2120 return;
2121 }
2122
Michael Kolb8872c232013-01-29 10:33:22 -08002123 setAutoExposureLockIfSupported();
2124 setAutoWhiteBalanceLockIfSupported();
2125 setFocusAreasIfSupported();
2126 setMeteringAreasIfSupported();
2127
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002128 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07002129 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002130 mCameraSettings
2131 .setFocusMode(mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Andy Huibers10c58162014-03-29 14:06:54 -07002132 SessionStatsCollector.instance().autofocusActive(
Angus Kong831347d2014-06-16 16:07:28 -07002133 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()) ==
2134 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE
2135 );
Michael Kolbd3253f22013-07-12 11:36:47 -07002136
Michael Kolb8872c232013-01-29 10:33:22 -08002137 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002138 updateParametersPictureSize();
2139
2140 // Set JPEG quality.
2141 updateParametersPictureQuality();
2142
2143 // For the following settings, we need to check if the settings are
2144 // still supported by latest driver, if not, ignore the settings.
2145
2146 // Set exposure compensation
2147 updateParametersExposureCompensation();
2148
2149 // Set the scene mode: also sets flash and white balance.
2150 updateParametersSceneMode();
2151
2152 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2153 updateAutoFocusMoveCallback();
2154 }
2155 }
2156
2157 private void updateParametersPictureSize() {
2158 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002159 String pictureSizeKey = isCameraFrontFacing() ? Keys.KEY_PICTURE_SIZE_FRONT
2160 : Keys.KEY_PICTURE_SIZE_BACK;
2161 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL,
2162 pictureSizeKey);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002163
Angus Kong6607dae2014-06-10 16:07:45 -07002164 List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Andy Huibers90c7ad52014-05-20 22:13:54 -07002165 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2166 mCameraDevice.getCameraId(), supported);
Angus Kong6607dae2014-06-10 16:07:45 -07002167 SettingsUtil.setCameraPictureSize(pictureSize, supported, mCameraSettings,
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002168 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002169
2170 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2171 mCameraDevice.getCameraId());
2172 if (ApiHelper.IS_NEXUS_5) {
2173 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2174 mShouldResizeTo16x9 = true;
2175 } else {
2176 mShouldResizeTo16x9 = false;
2177 }
2178 }
Michael Kolb8872c232013-01-29 10:33:22 -08002179
2180 // Set a preview size that is closest to the viewfinder height and has
2181 // the right aspect ratio.
Angus Kong6607dae2014-06-10 16:07:45 -07002182 List<Size> sizes = mCameraCapabilities.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07002183 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002184 (double) size.width() / size.height());
Angus Kong6607dae2014-06-10 16:07:45 -07002185 Size original = mCameraSettings.getCurrentPreviewSize();
2186 if (!optimalSize.equals(original)) {
Alan Newberger2631a152014-09-24 14:23:30 -07002187 Log.v(TAG, "setting preview size. optimal: " + optimalSize + "original: " + original);
Angus Kong6607dae2014-06-10 16:07:45 -07002188 mCameraSettings.setPreviewSize(optimalSize);
Doris Liu6432cd62013-06-13 17:20:31 -07002189
Michael Kolb8872c232013-01-29 10:33:22 -08002190 // Zoom related settings will be changed for different preview
2191 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002192 if (mHandler.getLooper() == Looper.myLooper()) {
Alan Newberger2631a152014-09-24 14:23:30 -07002193 Log.v(TAG, "matched looper, setting up preview");
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002194 // On UI thread only, not when camera starts up
2195 setupPreview();
2196 } else {
Alan Newberger2631a152014-09-24 14:23:30 -07002197 Log.v(TAG, "no looper match, directly applying settings");
Angus Kong6607dae2014-06-10 16:07:45 -07002198 mCameraDevice.applySettings(mCameraSettings);
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002199 }
Angus Kong6607dae2014-06-10 16:07:45 -07002200 mCameraSettings = mCameraDevice.getSettings();
Michael Kolb8872c232013-01-29 10:33:22 -08002201 }
Doris Liu95405742013-11-05 15:25:26 -08002202
Angus Kong00b7b102014-04-24 15:46:52 -07002203 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
Alan Newberger73514152014-09-10 15:03:27 -07002204 Log.v(TAG, "updating aspect ratio");
Angus Kong00b7b102014-04-24 15:46:52 -07002205 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2206 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002207 }
Alan Newberger73514152014-09-10 15:03:27 -07002208 Log.d(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002209 }
Michael Kolb8872c232013-01-29 10:33:22 -08002210
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002211 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002212 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2213 CameraProfile.QUALITY_HIGH);
Angus Kong6607dae2014-06-10 16:07:45 -07002214 mCameraSettings.setPhotoJpegCompressionQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002215 }
Michael Kolb8872c232013-01-29 10:33:22 -08002216
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002217 private void updateParametersExposureCompensation() {
2218 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002219 if (settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL,
2220 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) {
2221 int value = settingsManager.getInteger(mAppController.getCameraScope(),
2222 Keys.KEY_EXPOSURE);
Angus Kong88289042014-04-22 16:39:42 -07002223 int max = mCameraCapabilities.getMaxExposureCompensation();
2224 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002225 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002226 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002227 } else {
2228 Log.w(TAG, "invalid exposure range: " + value);
2229 }
Michael Kolb8872c232013-01-29 10:33:22 -08002230 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002231 // If exposure compensation is not enabled, reset the exposure compensation value.
2232 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002233 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002234
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002235 }
2236
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002237 private void updateParametersSceneMode() {
Angus Kong6607dae2014-06-10 16:07:45 -07002238 CameraCapabilities.Stringifier stringifier = mCameraCapabilities.getStringifier();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002239 SettingsManager settingsManager = mActivity.getSettingsManager();
2240
Erin Dahlgren6190c362014-06-13 14:12:08 -07002241 mSceneMode = stringifier.
2242 sceneModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2243 Keys.KEY_SCENE_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002244 if (mCameraCapabilities.supports(mSceneMode)) {
2245 if (mCameraSettings.getCurrentSceneMode() != mSceneMode) {
2246 mCameraSettings.setSceneMode(mSceneMode);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002247
2248 // Setting scene mode will change the settings of flash mode,
2249 // white balance, and focus mode. Here we read back the
2250 // parameters, so we can know those settings.
Angus Kong6607dae2014-06-10 16:07:45 -07002251 mCameraDevice.applySettings(mCameraSettings);
2252 mCameraSettings = mCameraDevice.getSettings();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002253 }
2254 } else {
Angus Kong6607dae2014-06-10 16:07:45 -07002255 mSceneMode = mCameraSettings.getCurrentSceneMode();
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002256 if (mSceneMode == null) {
Angus Kong6607dae2014-06-10 16:07:45 -07002257 mSceneMode = CameraCapabilities.SceneMode.AUTO;
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002258 }
2259 }
2260
Angus Kong6607dae2014-06-10 16:07:45 -07002261 if (CameraCapabilities.SceneMode.AUTO == mSceneMode) {
Michael Kolb8872c232013-01-29 10:33:22 -08002262 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002263 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002264
Michael Kolb8872c232013-01-29 10:33:22 -08002265 // Set focus mode.
2266 mFocusManager.overrideFocusMode(null);
Angus Kong831347d2014-06-16 16:07:28 -07002267 mCameraSettings.setFocusMode(
2268 mFocusManager.getFocusMode(mCameraSettings.getCurrentFocusMode()));
Michael Kolb8872c232013-01-29 10:33:22 -08002269 } else {
Angus Kong831347d2014-06-16 16:07:28 -07002270 mFocusManager.overrideFocusMode(mCameraSettings.getCurrentFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08002271 }
Michael Kolb8872c232013-01-29 10:33:22 -08002272 }
2273
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002274 private void updateParametersFlashMode() {
2275 SettingsManager settingsManager = mActivity.getSettingsManager();
2276
Angus Kong6607dae2014-06-10 16:07:45 -07002277 CameraCapabilities.FlashMode flashMode = mCameraCapabilities.getStringifier()
Erin Dahlgren6190c362014-06-13 14:12:08 -07002278 .flashModeFromString(settingsManager.getString(mAppController.getCameraScope(),
2279 Keys.KEY_FLASH_MODE));
Angus Kong6607dae2014-06-10 16:07:45 -07002280 if (mCameraCapabilities.supports(flashMode)) {
2281 mCameraSettings.setFlashMode(flashMode);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002282 }
2283 }
2284
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002285 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002286 private void updateAutoFocusMoveCallback() {
Angus Kong6607dae2014-06-10 16:07:45 -07002287 if (mCameraSettings.getCurrentFocusMode() ==
2288 CameraCapabilities.FocusMode.CONTINUOUS_PICTURE) {
Angus Kong9ef99252013-07-18 18:04:19 -07002289 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002290 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002291 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002292 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002293 }
2294 }
2295
Spike Spragueabf54e22014-03-27 15:41:28 -07002296 /**
2297 * Sets the exposure compensation to the given value and also updates settings.
2298 *
2299 * @param value exposure compensation value to be set
2300 */
2301 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002302 int max = mCameraCapabilities.getMaxExposureCompensation();
2303 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002304 if (value >= min && value <= max) {
Angus Kong6607dae2014-06-10 16:07:45 -07002305 mCameraSettings.setExposureCompensationIndex(value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002306 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren6190c362014-06-13 14:12:08 -07002307 settingsManager.set(mAppController.getCameraScope(),
2308 Keys.KEY_EXPOSURE, value);
Spike Spragueabf54e22014-03-27 15:41:28 -07002309 } else {
2310 Log.w(TAG, "invalid exposure range: " + value);
2311 }
2312 }
2313
Michael Kolb8872c232013-01-29 10:33:22 -08002314 // We separate the parameters into several subsets, so we can update only
2315 // the subsets actually need updating. The PREFERENCE set needs extra
2316 // locking because the preference can be changed from GLThread as well.
2317 private void setCameraParameters(int updateSet) {
2318 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2319 updateCameraParametersInitialize();
2320 }
2321
2322 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2323 updateCameraParametersZoom();
2324 }
2325
2326 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002327 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002328 }
2329
Spike Sprague5cc48d62014-09-22 10:52:32 -07002330 // some monkey tests can get here when shutting the app down
2331 // make sure mCameraDevice is still valid, b/17604028
2332 if (mCameraDevice != null) {
2333 mCameraDevice.applySettings(mCameraSettings);
2334 }
Michael Kolb8872c232013-01-29 10:33:22 -08002335 }
2336
2337 // If the Camera is idle, update the parameters immediately, otherwise
2338 // accumulate them in mUpdateSet and update later.
2339 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2340 mUpdateSet |= additionalUpdateSet;
2341 if (mCameraDevice == null) {
2342 // We will update all the parameters when we open the device, so
2343 // we don't need to do anything now.
2344 mUpdateSet = 0;
2345 return;
2346 } else if (isCameraIdle()) {
2347 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002348 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002349 mUpdateSet = 0;
2350 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002351 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2352 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002353 }
2354 }
2355 }
2356
ztenghui7b265a62013-09-09 14:58:44 -07002357 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002358 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002359 return (mCameraState == IDLE) ||
2360 (mCameraState == PREVIEW_STOPPED) ||
2361 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002362 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002363 }
2364
ztenghui7b265a62013-09-09 14:58:44 -07002365 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002366 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002367 String action = mActivity.getIntent().getAction();
2368 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002369 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002370 }
2371
2372 private void setupCaptureParams() {
2373 Bundle myExtras = mActivity.getIntent().getExtras();
2374 if (myExtras != null) {
2375 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2376 mCropValue = myExtras.getString("crop");
2377 }
2378 }
2379
Michael Kolb8872c232013-01-29 10:33:22 -08002380 private void initializeCapabilities() {
Angus Kong88289042014-04-22 16:39:42 -07002381 mCameraCapabilities = mCameraDevice.getCapabilities();
2382 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2383 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2384 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2385 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kong6607dae2014-06-10 16:07:45 -07002386 mContinuousFocusSupported =
2387 mCameraCapabilities.supports(CameraCapabilities.FocusMode.CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002388 }
2389
Michael Kolb8872c232013-01-29 10:33:22 -08002390 @Override
Sol Boucher2192fba2014-08-19 17:24:07 -07002391 public void onZoomChanged(float ratio) {
Michael Kolbd6954f32013-03-08 20:43:01 -08002392 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002393 if (mPaused) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002394 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002395 }
Sol Boucher2192fba2014-08-19 17:24:07 -07002396 mZoomValue = ratio;
Angus Kong6607dae2014-06-10 16:07:45 -07002397 if (mCameraSettings == null || mCameraDevice == null) {
Sol Boucher2192fba2014-08-19 17:24:07 -07002398 return;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002399 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002400 // Set zoom parameters asynchronously
Sol Boucher2192fba2014-08-19 17:24:07 -07002401 mCameraSettings.setZoomRatio(mZoomValue);
Angus Kong6607dae2014-06-10 16:07:45 -07002402 mCameraDevice.applySettings(mCameraSettings);
Angus Kongce5480e2013-01-29 17:43:48 -08002403 }
2404
2405 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002406 public int getCameraState() {
2407 return mCameraState;
2408 }
2409
2410 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002411 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002412 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002413 }
Angus Kong86d36312013-01-31 18:22:44 -08002414
2415 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002416 public void onLowMemory() {
2417 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002418 }
Angus Kong0d00a892013-03-26 11:40:40 -07002419
2420 @Override
2421 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2422 }
2423
2424 @Override
2425 public void onSensorChanged(SensorEvent event) {
2426 int type = event.sensor.getType();
2427 float[] data;
2428 if (type == Sensor.TYPE_ACCELEROMETER) {
2429 data = mGData;
2430 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2431 data = mMData;
2432 } else {
2433 // we should not be here.
2434 return;
2435 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002436 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002437 data[i] = event.values[i];
2438 }
2439 float[] orientation = new float[3];
2440 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2441 SensorManager.getOrientation(mR, orientation);
2442 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2443 if (mHeading < 0) {
2444 mHeading += 360;
2445 }
Angus Kong0d00a892013-03-26 11:40:40 -07002446 }
Doris Liu6432cd62013-06-13 17:20:31 -07002447
Ruben Brunkd217ed02013-10-08 23:31:13 -07002448 // For debugging only.
2449 public void setDebugUri(Uri uri) {
2450 mDebugUri = uri;
2451 }
2452
2453 // For debugging only.
2454 private void saveToDebugUri(byte[] data) {
2455 if (mDebugUri != null) {
2456 OutputStream outputStream = null;
2457 try {
2458 outputStream = mContentResolver.openOutputStream(mDebugUri);
2459 outputStream.write(data);
2460 outputStream.close();
2461 } catch (IOException e) {
2462 Log.e(TAG, "Exception while writing debug jpeg file", e);
2463 } finally {
2464 CameraUtil.closeSilently(outputStream);
2465 }
2466 }
2467 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002468
2469 @Override
2470 public void onRemoteShutterPress() {
Sascha Haeberling57d4f742014-06-25 11:05:49 -07002471 mHandler.post(new Runnable() {
2472 @Override
2473 public void run() {
2474 focusAndCapture();
2475 }
2476 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002477 }
Spike Spragueeeeed4f2014-08-27 12:01:36 -07002478
2479 /**
2480 * This class manages the loading/releasing/playing of the sounds needed for
2481 * countdown timer.
2482 */
2483 private class CountdownSoundPlayer {
2484 private SoundPool mSoundPool;
2485 private int mTimerIncrement;
2486 private int mTimerFinalSecond;
2487
2488 void loadSounds() {
2489 // Load the sounds.
2490 if (mSoundPool == null) {
2491 mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2492 mTimerIncrement = mSoundPool.load(mAppController.getAndroidContext(), R.raw.timer_increment, 1);
2493 mTimerFinalSecond = mSoundPool.load(mAppController.getAndroidContext(), R.raw.timer_final_second, 1);
2494 }
2495 }
2496
2497 void onRemainingSecondsChanged(int newVal) {
2498 if (mSoundPool == null) {
2499 Log.e(TAG, "Cannot play sound - they have not been loaded.");
2500 return;
2501 }
2502 if (newVal == 1) {
2503 mSoundPool.play(mTimerFinalSecond, 1.0f, 1.0f, 0, 0, 1.0f);
2504 } else if (newVal == 2 || newVal == 3) {
2505 mSoundPool.play(mTimerIncrement, 1.0f, 1.0f, 0, 0, 1.0f);
2506 }
2507 }
2508
2509 void release() {
2510 if (mSoundPool != null) {
2511 mSoundPool.release();
2512 mSoundPool = null;
2513 }
2514 }
2515 }
Michael Kolb8872c232013-01-29 10:33:22 -08002516}