blob: fcfb8486e33623e47fa9361cf6582374ed75f7ad [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;
25import android.graphics.SurfaceTexture;
26import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080027import android.hardware.Camera.Parameters;
Angus Kong0d00a892013-03-26 11:40:40 -070028import android.hardware.Sensor;
29import android.hardware.SensorEvent;
30import android.hardware.SensorEventListener;
31import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080032import android.location.Location;
33import android.media.CameraProfile;
34import android.net.Uri;
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -070035import android.os.AsyncTask;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070036import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080038import android.os.Handler;
39import android.os.Looper;
40import android.os.Message;
41import android.os.MessageQueue;
42import android.os.SystemClock;
43import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080044import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080045import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080046import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080047
Sameer Padala2c8cc452013-11-05 18:49:12 -080048import com.android.camera.PhotoModule.NamedImages.NamedEntity;
49import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080050import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070051import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070052import com.android.camera.app.LocationManager;
53import com.android.camera.app.MediaSaver;
54import com.android.camera.app.MemoryManager;
55import com.android.camera.app.MemoryManager.MemoryListener;
Angus Kong88289042014-04-22 16:39:42 -070056import com.android.camera.cameradevice.CameraCapabilities;
Angus Kong1045fef2014-04-21 11:17:37 -070057import com.android.camera.cameradevice.CameraManager.CameraAFCallback;
58import com.android.camera.cameradevice.CameraManager.CameraAFMoveCallback;
59import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
60import com.android.camera.cameradevice.CameraManager.CameraProxy;
61import com.android.camera.cameradevice.CameraManager.CameraShutterCallback;
Angus Kong2bca2102014-03-11 16:27:30 -070062import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070063import com.android.camera.exif.ExifInterface;
64import com.android.camera.exif.ExifTag;
65import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080066import com.android.camera.hardware.HardwareSpec;
67import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080068import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080069import com.android.camera.remote.RemoteCameraModule;
Erin Dahlgren357b7672013-11-20 17:38:14 -080070import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010071import com.android.camera.settings.SettingsUtil;
Angus Kongdcccc512013-08-08 17:06:03 -070072import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070073import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070074import com.android.camera.util.GcamHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070075import com.android.camera.util.SessionStatsCollector;
Angus Kong63424662014-04-23 10:47:47 -070076import com.android.camera.util.Size;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070077import com.android.camera.util.UsageStatistics;
78import com.android.camera2.R;
Seth Raphael5e09d012013-12-18 13:45:03 -080079import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080080
Angus Kongdcccc512013-08-08 17:06:03 -070081import java.io.File;
82import java.io.FileNotFoundException;
83import java.io.FileOutputStream;
84import java.io.IOException;
85import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010086import java.lang.ref.WeakReference;
Angus Kongdcccc512013-08-08 17:06:03 -070087import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070088import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070089
Michael Kolb8872c232013-01-29 10:33:22 -080090public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080091 extends CameraModule
92 implements PhotoController,
93 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -080094 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080095 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -080096 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080097 SettingsManager.OnSettingChangedListener,
98 RemoteCameraModule {
Michael Kolb8872c232013-01-29 10:33:22 -080099
Angus Kong2bca2102014-03-11 16:27:30 -0700100 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
Michael Kolb8872c232013-01-29 10:33:22 -0800101
102 // We number the request code from 1000 to avoid collision with Gallery.
103 private static final int REQUEST_CROP = 1000;
104
Angus Kong13e87c42013-11-25 10:02:47 -0800105 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800106 private static final int MSG_FIRST_TIME_INIT = 1;
107 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800108
109 // The subset of parameters we need to update in setCameraParameters().
110 private static final int UPDATE_PARAM_INITIALIZE = 1;
111 private static final int UPDATE_PARAM_ZOOM = 2;
112 private static final int UPDATE_PARAM_PREFERENCE = 4;
113 private static final int UPDATE_PARAM_ALL = -1;
114
Andy Huibersdef975d2013-11-22 09:13:39 -0800115 // This is the delay before we execute onResume tasks when coming
116 // from the lock screen, to allow time for onPause to execute.
117 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800118
Ruben Brunkd7488272013-10-10 18:45:53 -0700119 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
120
Michael Kolb8872c232013-01-29 10:33:22 -0800121 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800122 private CameraProxy mCameraDevice;
123 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700124 private CameraCapabilities mCameraCapabilities;
Michael Kolb8872c232013-01-29 10:33:22 -0800125 private Parameters mParameters;
126 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800127
128 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800129
Michael Kolb8872c232013-01-29 10:33:22 -0800130 // The activity is going to switch to the specified camera id. This is
131 // needed because texture copy is done in GL thread. -1 means camera is not
132 // switching.
133 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800134
135 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
136 // needed to be updated in mUpdateSet.
137 private int mUpdateSet;
138
139 private static final int SCREEN_DELAY = 2 * 60 * 1000;
140
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800141 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800142
143 private Parameters mInitialParams;
144 private boolean mFocusAreaSupported;
145 private boolean mMeteringAreaSupported;
146 private boolean mAeLockSupported;
147 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700148 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800149
150 // The degrees of the device rotated clockwise from its natural orientation.
151 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800152
153 private static final String sTempCropFilename = "crop-temp";
154
Michael Kolb8872c232013-01-29 10:33:22 -0800155 private boolean mFaceDetectionStarted = false;
156
Michael Kolb8872c232013-01-29 10:33:22 -0800157 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
158 private String mCropValue;
159 private Uri mSaveUri;
160
Ruben Brunkd217ed02013-10-08 23:31:13 -0700161 private Uri mDebugUri;
162
Angus Kongce5480e2013-01-29 17:43:48 -0800163 // We use a queue to generated names of the images to be used later
164 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800165 private NamedImages mNamedImages;
166
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800167 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800168 @Override
169 public void run() {
170 onShutterButtonClick();
171 }
172 };
173
Michael Kolb8872c232013-01-29 10:33:22 -0800174 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800175 * An unpublished intent flag requesting to return as soon as capturing is
176 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800177 */
178 private static final String EXTRA_QUICK_CAPTURE =
179 "android.intent.extra.quickCapture";
180
181 // The display rotation in degrees. This is only valid when mCameraState is
182 // not PREVIEW_STOPPED.
183 private int mDisplayRotation;
184 // The value for android.hardware.Camera.setDisplayOrientation.
185 private int mCameraDisplayOrientation;
186 // The value for UI components like indicators.
187 private int mDisplayOrientation;
188 // The value for android.hardware.Camera.Parameters.setRotation.
189 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700190 // Indicates whether we are using front camera
191 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800192 private boolean mFirstTimeInitialized;
193 private boolean mIsImageCaptureIntent;
194
Michael Kolb8872c232013-01-29 10:33:22 -0800195 private int mCameraState = PREVIEW_STOPPED;
196 private boolean mSnapshotOnIdle = false;
197
198 private ContentResolver mContentResolver;
199
200 private LocationManager mLocationManager;
Doris Liu2b906b82013-12-10 16:34:08 -0800201 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800202
Michael Kolb8872c232013-01-29 10:33:22 -0800203 private final PostViewPictureCallback mPostViewPictureCallback =
204 new PostViewPictureCallback();
205 private final RawPictureCallback mRawPictureCallback =
206 new RawPictureCallback();
207 private final AutoFocusCallback mAutoFocusCallback =
208 new AutoFocusCallback();
209 private final Object mAutoFocusMoveCallback =
210 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700211 ? new AutoFocusMoveCallback()
212 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800213
214 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
215
216 private long mFocusStartTime;
217 private long mShutterCallbackTime;
218 private long mPostViewPictureCallbackTime;
219 private long mRawPictureCallbackTime;
220 private long mJpegPictureCallbackTime;
221 private long mOnResumeTime;
222 private byte[] mJpegImageData;
223
224 // These latency time are for the CameraLatency test.
225 public long mAutoFocusTime;
226 public long mShutterLag;
227 public long mShutterToPictureDisplayedTime;
228 public long mPictureDisplayedToJpegCallbackTime;
229 public long mJpegCallbackFinishTime;
230 public long mCaptureStartTime;
231
232 // This handles everything about focus.
233 private FocusOverlayManager mFocusManager;
234
Doris Liubd1b8f92014-01-03 17:59:51 -0800235 private final int mGcamModeIndex;
Doris Liubd1b8f92014-01-03 17:59:51 -0800236
Michael Kolb8872c232013-01-29 10:33:22 -0800237 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800238
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100239 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800240
Michael Kolb8872c232013-01-29 10:33:22 -0800241 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700242 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800243 private final float[] mGData = new float[3];
244 private final float[] mMData = new float[3];
245 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700246 private int mHeading = -1;
247
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800248 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700249 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700250
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800251 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800252 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800253 @Override
254 public void onMediaSaved(Uri uri) {
255 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700256 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800257 }
258 }
259 };
Michael Kolb8872c232013-01-29 10:33:22 -0800260
Angus Kong0b9eb5b2014-04-30 15:03:33 -0700261 private final Runnable mResumeTaskRunnable = new Runnable() {
262 @Override
263 public void run() {
264 onResumeTasks();
265 }
266 };
267
Angus Kongdcccc512013-08-08 17:06:03 -0700268 private void checkDisplayRotation() {
269 // Set the display orientation if display rotation has changed.
270 // Sometimes this happens when the device is held upside
271 // down and camera app is opened. Rotation animation will
272 // take some time and the rotation value we have got may be
273 // wrong. Framework does not have a callback for this now.
274 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
275 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800276 }
Angus Kongdcccc512013-08-08 17:06:03 -0700277 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
278 mHandler.postDelayed(new Runnable() {
279 @Override
280 public void run() {
281 checkDisplayRotation();
282 }
283 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800284 }
285 }
286
287 /**
288 * This Handler is used to post message back onto the main thread of the
289 * application
290 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100291 private static class MainHandler extends Handler {
292 private final WeakReference<PhotoModule> mModule;
293
294 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800295 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100296 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800297 }
298
Michael Kolb8872c232013-01-29 10:33:22 -0800299 @Override
300 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100301 PhotoModule module = mModule.get();
302 if (module == null) {
303 return;
304 }
Michael Kolb8872c232013-01-29 10:33:22 -0800305 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800306 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100307 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800308 break;
309 }
310
Angus Kong13e87c42013-11-25 10:02:47 -0800311 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100312 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800313 break;
314 }
Michael Kolb8872c232013-01-29 10:33:22 -0800315 }
316 }
317 }
318
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700319 private void switchToGcamCapture() {
320 if (mActivity != null && mGcamModeIndex != 0) {
321 SettingsManager settingsManager = mActivity.getSettingsManager();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700322 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700323 SettingsManager.VALUE_ON);
324
325 // Disable the HDR+ button to prevent callbacks from being
326 // queued before the correct callback is attached to the button
327 // in the new module. The new module will set the enabled/disabled
328 // of this button when the module's preferred camera becomes available.
329 ButtonManager buttonManager = mActivity.getButtonManager();
330 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
331
332 // Do not post this to avoid this module switch getting interleaved with
333 // other button callbacks.
334 mActivity.onModeSelected(mGcamModeIndex);
335 }
336 }
337
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800338 /**
339 * Constructs a new photo module.
340 */
Angus Kongc4e66562013-11-22 23:03:21 -0800341 public PhotoModule(AppController app) {
342 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800343 mGcamModeIndex = app.getAndroidContext().getResources()
344 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800345 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700346
Michael Kolb8872c232013-01-29 10:33:22 -0800347 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100348 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
349 mActivity = activity;
350 // TODO: Need to look at the controller interface to see if we can get
351 // rid of passing in the activity directly.
352 mAppController = mActivity;
353
354 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
355 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800356
357 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800358 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800359
360 mContentResolver = mActivity.getContentResolver();
361
Michael Kolb8872c232013-01-29 10:33:22 -0800362 // Surface texture is from camera screen nail and startPreview needs it.
363 // This must be done before startPreview.
364 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800365
Angus Kong20fad242013-11-11 18:23:46 -0800366 mActivity.getCameraProvider().requestCamera(mCameraId);
367
Michael Kolb8872c232013-01-29 10:33:22 -0800368 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800369 mLocationManager = mActivity.getLocationManager();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800370 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800371 }
372
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800373 @Override
374 public boolean isUsingBottomBar() {
375 return true;
376 }
377
Michael Kolbd6954f32013-03-08 20:43:01 -0800378 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800379 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800380 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800381 setupCaptureParams();
382 }
383 }
384
385 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800386 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800387 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800388 startFaceDetection();
389 locationFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800390 }
391
Michael Kolb8872c232013-01-29 10:33:22 -0800392 // Prompt the user to pick to record location for the very first run of
393 // camera only
394 private void locationFirstRun() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800395 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100396
Erin Dahlgren4569b702014-02-24 14:21:11 -0800397 if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION)) {
398 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800399 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800400 if (mActivity.isSecureCamera()) {
401 return;
402 }
Michael Kolb8872c232013-01-29 10:33:22 -0800403 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800404 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800405 if (backCameraId == -1) {
406 // If there is no back camera, do not show the prompt.
407 return;
408 }
Doris Liu6a83d522013-07-02 12:03:32 -0700409 mUI.showLocationDialog();
410 }
Michael Kolb8872c232013-01-29 10:33:22 -0800411
ztenghui7b265a62013-09-09 14:58:44 -0700412 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700413 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800414 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700415 }
416
417 @Override
418 public void onPreviewUIDestroyed() {
419 if (mCameraDevice == null) {
420 return;
421 }
422 mCameraDevice.setPreviewTexture(null);
423 stopPreview();
424 }
425
Doris Liu1dfe7822013-12-12 00:02:08 -0800426 @Override
427 public void startPreCaptureAnimation() {
428 mAppController.startPreCaptureAnimation();
429 }
430
Michael Kolbd6954f32013-03-08 20:43:01 -0800431 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800432 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800433 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800434 }
435
Michael Kolbd6954f32013-03-08 20:43:01 -0800436 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800437 if (mPaused) {
438 return;
439 }
Erin Dahlgren357b7672013-11-20 17:38:14 -0800440 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800441
Alan Newbergerd41766f2014-04-09 18:25:34 -0700442 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800443 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800444 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800445 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800446 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800447 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800448 if (mFocusManager != null) {
449 mFocusManager.removeMessages();
450 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800451
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700452 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700453 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700454 // Start switch camera animation. Post a message because
455 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800456 }
457
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800458 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100459 new ButtonManager.ButtonCallback() {
460 @Override
461 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800462 // At the time this callback is fired, the camera id
463 // has be set to the desired camera.
464
Angus Kong97e282a2014-03-04 18:44:49 -0800465 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100466 return;
467 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800468 // If switching to back camera, and HDR+ is still on,
469 // switch back to gcam, otherwise handle callback normally.
470 SettingsManager settingsManager = mActivity.getSettingsManager();
471 if (settingsManager.isCameraBackFacing()) {
472 if (settingsManager.requestsReturnToHdrPlus()) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700473 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800474 return;
475 }
476 }
477
Sascha Haeberlingde303232014-02-07 02:30:53 +0100478 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800479
Alan Newbergerd41766f2014-04-09 18:25:34 -0700480 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100481 // We need to keep a preview frame for the animation before
482 // releasing the camera. This will trigger
483 // onPreviewTextureCopied.
484 // TODO: Need to animate the camera switch
485 switchCamera();
486 }
487 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800488
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800489 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100490 new ButtonManager.ButtonCallback() {
491 @Override
492 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700493 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100494 if (GcamHelper.hasGcamCapture()) {
495 // Set the camera setting to default backfacing.
Sascha Haeberlingde303232014-02-07 02:30:53 +0100496 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700497 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100498 } else {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700499 if (settingsManager.isHdrOn()) {
500 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
501 CameraUtil.SCENE_MODE_HDR);
502 } else {
503 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
504 Parameters.SCENE_MODE_AUTO);
505 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100506 updateParametersSceneMode();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700507 mCameraDevice.setParameters(mParameters);
508 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100509 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800510 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100511 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800512
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800513 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
514 @Override
515 public void onClick(View v) {
516 onCaptureCancelled();
517 }
518 };
519
520 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
521 @Override
522 public void onClick(View v) {
523 onCaptureDone();
524 }
525 };
526
527 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
528 @Override
529 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800530 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800531 onCaptureRetake();
532 }
533 };
534
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800535 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700536 public void hardResetSettings(SettingsManager settingsManager) {
537 // PhotoModule should hard reset HDR+ to off.
538 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
539 }
540
541 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800542 public HardwareSpec getHardwareSpec() {
Erin Dahlgren49ab9222014-01-28 17:40:28 -0800543 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800544 }
545
546 @Override
547 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
548 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
549
550 bottomBarSpec.enableCamera = true;
551 bottomBarSpec.cameraCallback = mCameraCallback;
552 bottomBarSpec.enableFlash = true;
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800553 bottomBarSpec.enableHdr = true;
554 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800555 bottomBarSpec.enableGridLines = true;
Spike Spragueabf54e22014-03-27 15:41:28 -0700556 bottomBarSpec.enableExposureCompensation = true;
557 bottomBarSpec.exposureCompensationSetCallback =
558 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
559 @Override
560 public void setExposure(int value) {
561 setExposureCompensation(value);
562 }
563 };
Angus Kong88289042014-04-22 16:39:42 -0700564 bottomBarSpec.minExposureCompensation = mCameraCapabilities.getMinExposureCompensation();
565 bottomBarSpec.maxExposureCompensation = mCameraCapabilities.getMaxExposureCompensation();
566 bottomBarSpec.exposureCompensationStep = mCameraCapabilities.getExposureCompensationStep();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800567
568 if (isImageCaptureIntent()) {
569 bottomBarSpec.showCancel = true;
570 bottomBarSpec.cancelCallback = mCancelCallback;
571 bottomBarSpec.showDone = true;
572 bottomBarSpec.doneCallback = mDoneCallback;
573 bottomBarSpec.showRetake = true;
574 bottomBarSpec.retakeCallback = mRetakeCallback;
575 }
576
577 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800578 }
579
Michael Kolbd6954f32013-03-08 20:43:01 -0800580 // either open a new camera or switch cameras
581 private void openCameraCommon() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800582 mUI.onCameraOpened(mParameters);
Angus Kong0fb819b2013-10-08 13:44:19 -0700583 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800584 // Set hdr plus to default: off.
585 SettingsManager settingsManager = mActivity.getSettingsManager();
586 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700587 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800588 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800589 }
590
ztenghui7b265a62013-09-09 14:58:44 -0700591 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800592 public void updatePreviewAspectRatio(float aspectRatio) {
593 mAppController.updatePreviewAspectRatio(aspectRatio);
594 }
595
Michael Kolb8872c232013-01-29 10:33:22 -0800596 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800597 SettingsManager settingsManager = mActivity.getSettingsManager();
598 if (settingsManager == null) {
599 Log.e(TAG, "Settings manager is null!");
600 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800601 }
Spike Spragueabf54e22014-03-27 15:41:28 -0700602 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
Michael Kolb8872c232013-01-29 10:33:22 -0800603 }
604
Michael Kolb8872c232013-01-29 10:33:22 -0800605 // Snapshots can only be taken after this is called. It should be called
606 // once only. We could have done these things in onCreate() but we want to
607 // make preview screen appear as soon as possible.
608 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700609 if (mFirstTimeInitialized || mPaused) {
610 return;
611 }
Michael Kolb8872c232013-01-29 10:33:22 -0800612
Michael Kolbd6954f32013-03-08 20:43:01 -0800613 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800614
Angus Kong86d36312013-01-31 18:22:44 -0800615 // We set the listener only when both service and shutterbutton
616 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800617 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800618
Michael Kolb8872c232013-01-29 10:33:22 -0800619 mNamedImages = new NamedImages();
620
621 mFirstTimeInitialized = true;
622 addIdleHandler();
623
Spike Spraguee6374b72014-04-25 17:24:32 -0700624 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800625 }
626
Michael Kolbd6954f32013-03-08 20:43:01 -0800627 // If the activity is paused and resumed, this method will be called in
628 // onResume.
629 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800630 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800631 mNamedImages = new NamedImages();
632 mUI.initializeSecondTime(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -0800633 }
634
Michael Kolb8872c232013-01-29 10:33:22 -0800635 private void addIdleHandler() {
636 MessageQueue queue = Looper.myQueue();
637 queue.addIdleHandler(new MessageQueue.IdleHandler() {
638 @Override
639 public boolean queueIdle() {
640 Storage.ensureOSXCompatible();
641 return false;
642 }
643 });
644 }
645
Michael Kolb8872c232013-01-29 10:33:22 -0800646 @Override
647 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800648 if (mFaceDetectionStarted) {
649 return;
650 }
Angus Kong88289042014-04-22 16:39:42 -0700651 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800652 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700653 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700654 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800655 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700656 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800657 }
658 }
659
Michael Kolb8872c232013-01-29 10:33:22 -0800660 @Override
661 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800662 if (!mFaceDetectionStarted) {
663 return;
664 }
Angus Kong88289042014-04-22 16:39:42 -0700665 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800666 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700667 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800668 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800669 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700670 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800671 }
672 }
673
Michael Kolb8872c232013-01-29 10:33:22 -0800674 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700675 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700676
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800677 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700678
Sascha Haeberling37f36112013-08-06 14:31:52 -0700679 public ShutterCallback(boolean needsAnimation) {
680 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700681 }
682
Michael Kolb8872c232013-01-29 10:33:22 -0800683 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700684 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800685 mShutterCallbackTime = System.currentTimeMillis();
686 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
687 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700688 if (mNeedsAnimation) {
689 mActivity.runOnUiThread(new Runnable() {
690 @Override
691 public void run() {
692 animateAfterShutter();
693 }
694 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700695 }
Michael Kolb8872c232013-01-29 10:33:22 -0800696 }
697 }
698
Angus Kong9ef99252013-07-18 18:04:19 -0700699 private final class PostViewPictureCallback
700 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800701 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800702 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800703 mPostViewPictureCallbackTime = System.currentTimeMillis();
704 Log.v(TAG, "mShutterToPostViewCallbackTime = "
705 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
706 + "ms");
707 }
708 }
709
Angus Kong9ef99252013-07-18 18:04:19 -0700710 private final class RawPictureCallback
711 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800712 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800713 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800714 mRawPictureCallbackTime = System.currentTimeMillis();
715 Log.v(TAG, "mShutterToRawCallbackTime = "
716 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
717 }
718 }
719
Angus Kong9ef99252013-07-18 18:04:19 -0700720 private final class JpegPictureCallback
721 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800722 Location mLocation;
723
724 public JpegPictureCallback(Location loc) {
725 mLocation = loc;
726 }
727
728 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800729 public void onPictureTaken(final byte[] jpegData, CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700730 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800731 if (mPaused) {
732 return;
733 }
Doris Liu6432cd62013-06-13 17:20:31 -0700734 if (mIsImageCaptureIntent) {
735 stopPreview();
736 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700737 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700738 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800739 }
740
741 mJpegPictureCallbackTime = System.currentTimeMillis();
742 // If postview callback has arrived, the captured image is displayed
743 // in postview callback. If not, the captured image is displayed in
744 // raw picture callback.
745 if (mPostViewPictureCallbackTime != 0) {
746 mShutterToPictureDisplayedTime =
747 mPostViewPictureCallbackTime - mShutterCallbackTime;
748 mPictureDisplayedToJpegCallbackTime =
749 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
750 } else {
751 mShutterToPictureDisplayedTime =
752 mRawPictureCallbackTime - mShutterCallbackTime;
753 mPictureDisplayedToJpegCallbackTime =
754 mJpegPictureCallbackTime - mRawPictureCallbackTime;
755 }
756 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
757 + mPictureDisplayedToJpegCallbackTime + "ms");
758
Michael Kolb8872c232013-01-29 10:33:22 -0800759 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
760 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700761 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800762 }
763
Doris Liu36e56fb2013-09-11 17:38:08 -0700764 ExifInterface exif = Exif.getExif(jpegData);
765 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -0700766
Andy Huiberse08bc042014-04-11 19:26:47 -0700767 float zoomValue = 0f;
Angus Kong88289042014-04-22 16:39:42 -0700768 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Andy Huiberse08bc042014-04-11 19:26:47 -0700769 int zoomIndex = mParameters.getZoom();
770 List<Integer> zoomRatios = mParameters.getZoomRatios();
771 if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
772 zoomValue = 0.01f * zoomRatios.get(zoomIndex);
773 }
774 }
Andy Huibers6b9743a2014-04-03 23:23:29 -0700775 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
Andy Huibers10c58162014-03-29 14:06:54 -0700776 UsageStatistics.instance().photoCaptureDoneEvent(
777 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
778 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers6b9743a2014-04-03 23:23:29 -0700779 isCameraFrontFacing(), hdrOn, zoomValue);
Ruben Brunkd217ed02013-10-08 23:31:13 -0700780
Ruben Brunkd7488272013-10-10 18:45:53 -0700781 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800782 // Calculate the width and the height of the jpeg.
Angus Kong63424662014-04-23 10:47:47 -0700783 Size s = new Size(mParameters.getPictureSize());
Michael Kolb8872c232013-01-29 10:33:22 -0800784 int width, height;
785 if ((mJpegRotation + orientation) % 180 == 0) {
Angus Kong00b7b102014-04-24 15:46:52 -0700786 width = s.width();
787 height = s.height();
Michael Kolb8872c232013-01-29 10:33:22 -0800788 } else {
Angus Kong00b7b102014-04-24 15:46:52 -0700789 width = s.height();
790 height = s.width();
Michael Kolb8872c232013-01-29 10:33:22 -0800791 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700792 NamedEntity name = mNamedImages.getNextNameEntity();
793 String title = (name == null) ? null : name.title;
794 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -0700795
796 // Handle debug mode outputs
797 if (mDebugUri != null) {
798 // If using a debug uri, save jpeg there.
799 saveToDebugUri(jpegData);
800
801 // Adjust the title of the debug image shown in mediastore.
802 if (title != null) {
803 title = DEBUG_IMAGE_PREFIX + title;
804 }
805 }
806
Michael Kolb8872c232013-01-29 10:33:22 -0800807 if (title == null) {
808 Log.e(TAG, "Unbalanced name/data pair");
809 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800810 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800811 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800812 }
Angus Kong0d00a892013-03-26 11:40:40 -0700813 if (mHeading >= 0) {
814 // heading direction has been updated by the sensor.
815 ExifTag directionRefTag = exif.buildTag(
816 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
817 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
818 ExifTag directionTag = exif.buildTag(
819 ExifInterface.TAG_GPS_IMG_DIRECTION,
820 new Rational(mHeading, 1));
821 exif.setTag(directionRefTag);
822 exif.setTag(directionTag);
823 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800824 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -0800825 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700826 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800827 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800828 // Animate capture with real jpeg data instead of a preview
829 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -0700830 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800831 } else {
832 mJpegImageData = jpegData;
833 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -0700834 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800835 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800836 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800837 }
838 }
839
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -0700840 // Send the taken photo to remote shutter listeners, if any are
841 // registered.
842 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
843 @Override
844 public void run() {
845 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
846 }
847 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800848
Michael Kolb8872c232013-01-29 10:33:22 -0800849 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800850 // latency. It's true that someone else could write to the SD card
851 // in the mean time and fill it, but that could have happened
852 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -0700853 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800854
855 long now = System.currentTimeMillis();
856 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
Andy Huibers10c58162014-03-29 14:06:54 -0700857 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
Michael Kolb8872c232013-01-29 10:33:22 -0800858 mJpegPictureCallbackTime = 0;
859 }
860 }
861
Angus Kong9ef99252013-07-18 18:04:19 -0700862 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800863 @Override
Andy Huibers10c58162014-03-29 14:06:54 -0700864 public void onAutoFocus(boolean focused, CameraProxy camera) {
865 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800866 if (mPaused) {
867 return;
868 }
Michael Kolb8872c232013-01-29 10:33:22 -0800869
870 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -0700871 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -0800872 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -0700873 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -0800874 }
875 }
876
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700877 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -0800878 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700879 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800880 @Override
881 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700882 boolean moving, CameraProxy camera) {
883 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -0700884 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -0800885 }
886 }
887
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700888 /**
889 * This class is just a thread-safe queue for name,date holder objects.
890 */
891 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800892 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -0800893
894 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700895 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -0800896 }
897
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700898 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -0800899 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700900 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -0800901 r.date = date;
902 mQueue.add(r);
903 }
904
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700905 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800906 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700907 if (!mQueue.isEmpty()) {
908 return mQueue.remove(0);
909 }
Michael Kolb8872c232013-01-29 10:33:22 -0800910 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700911 return null;
Michael Kolb8872c232013-01-29 10:33:22 -0800912 }
913
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700914 public static class NamedEntity {
915 public String title;
916 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -0800917 }
918 }
919
920 private void setCameraState(int state) {
921 mCameraState = state;
922 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -0800923 case PREVIEW_STOPPED:
924 case SNAPSHOT_IN_PROGRESS:
925 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -0800926 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700927 break;
928 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -0800929 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700930 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800931 }
932 }
933
Sascha Haeberling37f36112013-08-06 14:31:52 -0700934 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800935 // Only animate when in full screen capture mode
936 // i.e. If monkey/a user swipes to the gallery during picture taking,
937 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700938 if (!mIsImageCaptureIntent) {
939 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700940 }
Michael Kolb8872c232013-01-29 10:33:22 -0800941 }
942
943 @Override
944 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800945 // If we are already in the middle of taking a snapshot or the image
946 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -0800947 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -0700948 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800949 return false;
950 }
951 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -0700952
Michael Kolb8872c232013-01-29 10:33:22 -0800953 mPostViewPictureCallbackTime = 0;
954 mJpegImageData = null;
955
Angus Kongb50b5cb2013-08-09 14:55:20 -0700956 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -0800957
958 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700959 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -0800960 }
961
962 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800963 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800964
Doris Liu3cf565c2013-02-15 10:55:37 -0800965 // We need to be consistent with the framework orientation (i.e. the
966 // orientation of the UI.) when the auto-rotate screen setting is on.
967 if (mActivity.isAutoRotateScreen()) {
968 orientation = (360 - mDisplayRotation) % 360;
969 } else {
970 orientation = mOrientation;
971 }
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +0100972 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
973 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800974 mParameters.setRotation(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -0800975 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700976 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -0800977 mCameraDevice.setParameters(mParameters);
978
Sascha Haeberling88901942013-08-28 17:49:00 -0700979 // We don't want user to press the button again while taking a
980 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -0700981 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -0700982 mCameraDevice.takePicture(mHandler,
983 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -0700984 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -0700985 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -0800986
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700987 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -0800988
989 mFaceDetectionStarted = false;
990 setCameraState(SNAPSHOT_IN_PROGRESS);
991 return true;
992 }
993
994 @Override
995 public void setFocusParameters() {
996 setCameraParameters(UPDATE_PARAM_PREFERENCE);
997 }
998
Michael Kolbd6954f32013-03-08 20:43:01 -0800999 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001000 // If scene mode is set, we cannot set flash mode, white balance, and
1001 // focus mode, instead, we read it from driver
1002 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001003 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001004 }
1005 }
1006
Doris Liu8ad8ad42014-03-27 14:43:31 -07001007 private void overrideCameraSettings(final String flashMode, final String focusMode) {
Erin Dahlgrene419b192013-12-03 13:10:27 -08001008 SettingsManager settingsManager = mActivity.getSettingsManager();
1009 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
Erin Dahlgrene419b192013-12-03 13:10:27 -08001010 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001011 }
1012
1013 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001014 public void onOrientationChanged(int orientation) {
1015 // We keep the last known orientation. So if the user first orient
1016 // the camera then point the camera to floor or sky, we still have
1017 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001018 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1019 return;
1020 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001021 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001022 }
1023
1024 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001025 public void onCameraAvailable(CameraProxy cameraProxy) {
1026 if (mPaused) {
1027 return;
1028 }
1029 mCameraDevice = cameraProxy;
1030
1031 initializeCapabilities();
1032
1033 // Reset zoom value index.
1034 mZoomValue = 0;
1035 if (mFocusManager == null) {
1036 initializeFocusManager();
1037 }
Angus Kong88289042014-04-22 16:39:42 -07001038 mFocusManager.setParameters(mInitialParams, mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001039
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001040 // Do camera parameter dependent initialization.
Angus Kong20fad242013-11-11 18:23:46 -08001041 mParameters = mCameraDevice.getParameters();
1042 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001043 // Set a listener which updates camera parameters based
1044 // on changed settings.
1045 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001046 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001047 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001048
Angus Kong20fad242013-11-11 18:23:46 -08001049 startPreview();
1050
1051 onCameraOpened();
1052 }
1053
1054 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001055 public void onCaptureCancelled() {
1056 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1057 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001058 }
1059
Michael Kolbd6954f32013-03-08 20:43:01 -08001060 @Override
1061 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001062 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001063 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001064 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001065 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001066 setupPreview();
1067 }
1068
Michael Kolbd6954f32013-03-08 20:43:01 -08001069 @Override
1070 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001071 if (mPaused) {
1072 return;
1073 }
1074
1075 byte[] data = mJpegImageData;
1076
1077 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001078 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001079 // caller specifies a "save uri" then write the data to its
1080 // stream. Otherwise, pass back a scaled down version of the bitmap
1081 // directly in the extras.
1082 if (mSaveUri != null) {
1083 OutputStream outputStream = null;
1084 try {
1085 outputStream = mContentResolver.openOutputStream(mSaveUri);
1086 outputStream.write(data);
1087 outputStream.close();
1088
1089 mActivity.setResultEx(Activity.RESULT_OK);
1090 mActivity.finish();
1091 } catch (IOException ex) {
1092 // ignore exception
1093 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001094 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001095 }
1096 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001097 ExifInterface exif = Exif.getExif(data);
1098 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001099 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1100 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001101 mActivity.setResultEx(Activity.RESULT_OK,
1102 new Intent("inline-data").putExtra("data", bitmap));
1103 mActivity.finish();
1104 }
1105 } else {
1106 // Save the image to a temp file and invoke the cropper
1107 Uri tempUri = null;
1108 FileOutputStream tempStream = null;
1109 try {
1110 File path = mActivity.getFileStreamPath(sTempCropFilename);
1111 path.delete();
1112 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1113 tempStream.write(data);
1114 tempStream.close();
1115 tempUri = Uri.fromFile(path);
1116 } catch (FileNotFoundException ex) {
1117 mActivity.setResultEx(Activity.RESULT_CANCELED);
1118 mActivity.finish();
1119 return;
1120 } catch (IOException ex) {
1121 mActivity.setResultEx(Activity.RESULT_CANCELED);
1122 mActivity.finish();
1123 return;
1124 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001125 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001126 }
1127
1128 Bundle newExtras = new Bundle();
1129 if (mCropValue.equals("circle")) {
1130 newExtras.putString("circleCrop", "true");
1131 }
1132 if (mSaveUri != null) {
1133 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1134 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001135 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001136 }
1137 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001138 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001139 }
1140
Sascha Haeberling37f36112013-08-06 14:31:52 -07001141 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001142 final String CROP_ACTION = "com.android.camera.action.CROP";
1143 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001144
1145 cropIntent.setData(tempUri);
1146 cropIntent.putExtras(newExtras);
1147
1148 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1149 }
1150 }
1151
Michael Kolb8872c232013-01-29 10:33:22 -08001152 @Override
1153 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001154 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001155 }
1156
1157 @Override
1158 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001159 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001160 || (mCameraState == PREVIEW_STOPPED)) {
1161 return;
1162 }
Michael Kolb8872c232013-01-29 10:33:22 -08001163
1164 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001165 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001166 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001167 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001168 return;
1169 }
Alan Newbergerd41766f2014-04-09 18:25:34 -07001170 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
Michael Kolb8872c232013-01-29 10:33:22 -08001171
Angus Kongb50b5cb2013-08-09 14:55:20 -07001172 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001173 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001174 }
Michael Kolb8872c232013-01-29 10:33:22 -08001175 // If the user wants to do a snapshot while the previous one is still
1176 // in progress, remember the fact and do it after we finish the previous
1177 // one and re-start the preview. Snapshot in progress also includes the
1178 // state that autofocus is focusing and a picture will be taken when
1179 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001180 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1181 if (!mIsImageCaptureIntent) {
1182 mSnapshotOnIdle = true;
1183 }
Michael Kolb8872c232013-01-29 10:33:22 -08001184 return;
1185 }
1186
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001187 mSnapshotOnIdle = false;
Angus Kongeaaf56d2014-02-20 11:59:10 -08001188 mFocusManager.focusAndCapture();
Michael Kolb8872c232013-01-29 10:33:22 -08001189 }
1190
Andy Huibersdef975d2013-11-22 09:13:39 -08001191 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001192 if (mPaused) {
1193 return;
1194 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001195 Log.v(TAG, "Executing onResumeTasks.");
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001196 CameraProvider camProvider = mActivity.getCameraProvider();
1197 if (camProvider == null) {
1198 // No camera provider, the Activity is destroyed already.
1199 return;
1200 }
1201 camProvider.requestCamera(mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -08001202
Michael Kolb8872c232013-01-29 10:33:22 -08001203 mJpegPictureCallbackTime = 0;
1204 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001205
1206 mOnResumeTime = SystemClock.uptimeMillis();
1207 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001208
1209 // If first time initialization is not finished, put it in the
1210 // message queue.
1211 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001212 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001213 } else {
1214 initializeSecondTime();
1215 }
Michael Kolb8872c232013-01-29 10:33:22 -08001216
Angus Kong0d00a892013-03-26 11:40:40 -07001217 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1218 if (gsensor != null) {
1219 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1220 }
1221
1222 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1223 if (msensor != null) {
1224 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1225 }
Michael Kolb8872c232013-01-29 10:33:22 -08001226 }
1227
Angus Kongc4e66562013-11-22 23:03:21 -08001228 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001229 * @return Whether the currently active camera is front-facing.
1230 */
1231 private boolean isCameraFrontFacing() {
1232 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1233 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1234 }
1235
1236 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001237 * The focus manager is the first UI related element to get initialized, and
1238 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001239 */
1240 private void initializeFocusManager() {
1241 // Create FocusManager object. startPreview needs it.
1242 // if mFocusManager not null, reuse it
1243 // otherwise create a new instance
1244 if (mFocusManager != null) {
1245 mFocusManager.removeMessages();
1246 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001247 mMirror = isCameraFrontFacing();
Angus Kongc4e66562013-11-22 23:03:21 -08001248 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1249 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001250 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1251 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001252 mInitialParams, this, mMirror,
Doris Liu482de022013-12-18 19:18:16 -08001253 mActivity.getMainLooper(), mUI.getFocusUI());
Angus Kongc4e66562013-11-22 23:03:21 -08001254 }
Doris Liu482de022013-12-18 19:18:16 -08001255 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001256 }
1257
1258 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001259 public void resume() {
1260 mPaused = false;
Doris Liu482de022013-12-18 19:18:16 -08001261 if (mFocusManager != null) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001262 // If camera is not open when resume is called, focus manager will
1263 // not
Doris Liu15b99612013-12-21 11:32:28 -08001264 // be initialized yet, in which case it will start listening to
1265 // preview area size change later in the initialization.
Doris Liu482de022013-12-18 19:18:16 -08001266 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1267 }
Doris Liu59401042014-01-14 17:51:32 -08001268
1269 if (mUI.getPreviewAreaSizeChangedListener() != null) {
1270 mAppController.addPreviewAreaSizeChangedListener(
1271 mUI.getPreviewAreaSizeChangedListener());
1272 }
1273
Angus Kongc4e66562013-11-22 23:03:21 -08001274 // Add delay on resume from lock screen only, in order to to speed up
1275 // the onResume --> onPause --> onResume cycle from lock screen.
1276 // Don't do always because letting go of thread can cause delay.
1277 String action = mActivity.getIntent().getAction();
1278 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1279 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1280 Log.v(TAG, "On resume, from lock screen.");
1281 // Note: onPauseAfterSuper() will delete this runnable, so we will
1282 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001283 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001284 } else {
1285 Log.v(TAG, "On resume.");
1286 onResumeTasks();
1287 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001288 getServices().getRemoteShutterListener().onModuleReady(this);
Andy Huibers10c58162014-03-29 14:06:54 -07001289 SessionStatsCollector.instance().sessionActive(true);
Angus Kongc4e66562013-11-22 23:03:21 -08001290 }
1291
1292 @Override
1293 public void pause() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001294 getServices().getRemoteShutterListener().onModuleExit();
Michael Kolb8872c232013-01-29 10:33:22 -08001295 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001296 mHandler.removeCallbacks(mResumeTaskRunnable);
Andy Huibers10c58162014-03-29 14:06:54 -07001297 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001298
Angus Kong0d00a892013-03-26 11:40:40 -07001299 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1300 if (gsensor != null) {
1301 mSensorManager.unregisterListener(this, gsensor);
1302 }
1303
1304 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1305 if (msensor != null) {
1306 mSensorManager.unregisterListener(this, msensor);
1307 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001308
Michael Kolb8872c232013-01-29 10:33:22 -08001309 // Reset the focus first. Camera CTS does not guarantee that
1310 // cancelAutoFocus is allowed after preview stops.
1311 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1312 mCameraDevice.cancelAutoFocus();
1313 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001314
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001315 // If the camera has not been opened asynchronously yet,
1316 // and startPreview hasn't been called, then this is a no-op.
1317 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001318 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001319
Angus Kongce5480e2013-01-29 17:43:48 -08001320 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001321
Michael Kolb8872c232013-01-29 10:33:22 -08001322 // If we are in an image capture intent and has taken
1323 // a picture, we just clear it in onPause.
1324 mJpegImageData = null;
1325
Angus Kongdcccc512013-08-08 17:06:03 -07001326 // Remove the messages and runnables in the queue.
1327 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001328
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001329 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001330 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001331 mUI.onPause();
1332
Michael Kolb8872c232013-01-29 10:33:22 -08001333 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001334 if (mFocusManager != null) {
1335 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001336 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001337 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001338 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu59401042014-01-14 17:51:32 -08001339 if (mUI.getPreviewAreaSizeChangedListener() != null) {
1340 mAppController.removePreviewAreaSizeChangedListener(
1341 mUI.getPreviewAreaSizeChangedListener());
1342 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001343
1344 SettingsManager settingsManager = mActivity.getSettingsManager();
1345 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001346 }
1347
Angus Kong20fad242013-11-11 18:23:46 -08001348 @Override
1349 public void destroy() {
1350 // TODO: implement this.
1351 }
1352
1353 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001354 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001355 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001356 }
1357
1358 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001359 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001360 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001361 setDisplayOrientation();
1362 }
1363 }
1364
Michael Kolb8872c232013-01-29 10:33:22 -08001365 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001366 return isCameraIdle()
1367 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001368 }
1369
1370 @Override
1371 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001372 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001373 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001374 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001375 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001376 setCameraState(FOCUSING);
1377 }
1378
1379 @Override
1380 public void cancelAutoFocus() {
1381 mCameraDevice.cancelAutoFocus();
1382 setCameraState(IDLE);
1383 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1384 }
1385
Michael Kolb8872c232013-01-29 10:33:22 -08001386 @Override
1387 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001388 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1389 || mCameraState == SNAPSHOT_IN_PROGRESS
1390 || mCameraState == SWITCHING_CAMERA
1391 || mCameraState == PREVIEW_STOPPED) {
1392 return;
1393 }
1394
1395 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001396 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1397 return;
1398 }
Doris Liu482de022013-12-18 19:18:16 -08001399 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001400 }
1401
1402 @Override
1403 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001404 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001405 }
1406
1407 @Override
1408 public boolean onKeyDown(int keyCode, KeyEvent event) {
1409 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001410 case KeyEvent.KEYCODE_VOLUME_UP:
1411 case KeyEvent.KEYCODE_VOLUME_DOWN:
1412 case KeyEvent.KEYCODE_FOCUS:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001413 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001414 if (event.getRepeatCount() == 0) {
1415 onShutterButtonFocus(true);
1416 }
1417 return true;
1418 }
1419 return false;
1420 case KeyEvent.KEYCODE_CAMERA:
1421 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1422 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001423 }
1424 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001425 case KeyEvent.KEYCODE_DPAD_CENTER:
1426 // If we get a dpad center event without any focused view, move
1427 // the focus to the shutter button and press it.
1428 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1429 // Start auto-focus immediately to reduce shutter lag. After
1430 // the shutter button gets the focus, onShutterButtonFocus()
1431 // will be called again but it is fine.
1432 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001433 }
1434 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001435 }
1436 return false;
1437 }
1438
1439 @Override
1440 public boolean onKeyUp(int keyCode, KeyEvent event) {
1441 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001442 case KeyEvent.KEYCODE_VOLUME_UP:
1443 case KeyEvent.KEYCODE_VOLUME_DOWN:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001444 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001445 onShutterButtonClick();
1446 return true;
1447 }
1448 return false;
1449 case KeyEvent.KEYCODE_FOCUS:
1450 if (mFirstTimeInitialized) {
1451 onShutterButtonFocus(false);
1452 }
Michael Kolb8872c232013-01-29 10:33:22 -08001453 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001454 }
1455 return false;
1456 }
1457
Michael Kolb8872c232013-01-29 10:33:22 -08001458 private void closeCamera() {
1459 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001460 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001461 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001462 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001463 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001464
Michael Kolb8872c232013-01-29 10:33:22 -08001465 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001466 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001467 mCameraDevice = null;
1468 setCameraState(PREVIEW_STOPPED);
1469 mFocusManager.onCameraReleased();
1470 }
1471 }
1472
1473 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001474 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1475 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001476 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001477 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001478 if (mFocusManager != null) {
1479 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1480 }
Doris Liu6432cd62013-06-13 17:20:31 -07001481 // Change the camera display orientation
1482 if (mCameraDevice != null) {
1483 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1484 }
Michael Kolb8872c232013-01-29 10:33:22 -08001485 }
1486
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001487 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001488 private void setupPreview() {
1489 mFocusManager.resetTouchFocus();
1490 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001491 }
1492
Angus Kong20fad242013-11-11 18:23:46 -08001493 /**
1494 * Returns whether we can/should start the preview or not.
1495 */
1496 private boolean checkPreviewPreconditions() {
1497 if (mPaused) {
1498 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001499 }
Michael Kolb8872c232013-01-29 10:33:22 -08001500
Angus Kong20fad242013-11-11 18:23:46 -08001501 if (mCameraDevice == null) {
1502 Log.w(TAG, "startPreview: camera device not ready yet.");
1503 return false;
1504 }
1505
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001506 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001507 if (st == null) {
1508 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001509 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001510 }
1511
1512 if (!mCameraPreviewParamsReady) {
1513 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001514 return false;
1515 }
1516 return true;
1517 }
1518
1519 /**
1520 * The start/stop preview should only run on the UI thread.
1521 */
1522 private void startPreview() {
1523 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001524 return;
1525 }
Angus Kong20fad242013-11-11 18:23:46 -08001526
Angus Kong2bca2102014-03-11 16:27:30 -07001527 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001528 setDisplayOrientation();
1529
1530 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001531 // If the focus mode is continuous autofocus, call cancelAutoFocus
1532 // to resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001533 String focusMode = mFocusManager.getFocusMode();
1534 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001535 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001536 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001537 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1538 }
1539 setCameraParameters(UPDATE_PARAM_ALL);
1540 // Let UI set its expected aspect ratio
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001541 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001542
Alan Newbergerd41766f2014-04-09 18:25:34 -07001543 Log.i(TAG, "startPreview");
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001544 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001545
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001546 mFocusManager.onPreviewStarted();
1547 onPreviewStarted();
Andy Huibers10c58162014-03-29 14:06:54 -07001548 SessionStatsCollector.instance().previewActive(true);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001549 if (mSnapshotOnIdle) {
1550 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001551 }
1552 }
1553
Michael Kolbd6954f32013-03-08 20:43:01 -08001554 @Override
1555 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001556 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001557 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001558 mCameraDevice.stopPreview();
1559 mFaceDetectionStarted = false;
1560 }
1561 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001562 if (mFocusManager != null) {
1563 mFocusManager.onPreviewStopped();
1564 }
Andy Huibers10c58162014-03-29 14:06:54 -07001565 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001566 }
1567
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001568 @Override
Erin Dahlgren1648c362014-01-06 15:06:04 -08001569 public void onSettingChanged(SettingsManager settingsManager, int id) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001570 switch (id) {
1571 case SettingsManager.SETTING_FLASH_MODE: {
1572 updateParametersFlashMode();
1573 break;
1574 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001575 default: {
1576 // Do nothing.
1577 }
1578 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001579
1580 if (mCameraDevice != null) {
1581 mCameraDevice.setParameters(mParameters);
1582 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001583 }
1584
Michael Kolb8872c232013-01-29 10:33:22 -08001585 private void updateCameraParametersInitialize() {
1586 // Reset preview frame rate to the maximum because it may be lowered by
1587 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07001588 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07001589 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001590 mParameters.setPreviewFpsRange(
1591 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1592 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001593 }
1594
Angus Kongb50b5cb2013-08-09 14:55:20 -07001595 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001596
1597 // Disable video stabilization. Convenience methods not available in API
1598 // level <= 14
1599 String vstabSupported = mParameters.get("video-stabilization-supported");
1600 if ("true".equals(vstabSupported)) {
1601 mParameters.set("video-stabilization", "false");
1602 }
1603 }
1604
1605 private void updateCameraParametersZoom() {
1606 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07001607 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001608 mParameters.setZoom(mZoomValue);
1609 }
1610 }
1611
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001612 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001613 private void setAutoExposureLockIfSupported() {
1614 if (mAeLockSupported) {
1615 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1616 }
1617 }
1618
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001619 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001620 private void setAutoWhiteBalanceLockIfSupported() {
1621 if (mAwbLockSupported) {
1622 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1623 }
1624 }
1625
Michael Kolb8872c232013-01-29 10:33:22 -08001626 private void setFocusAreasIfSupported() {
1627 if (mFocusAreaSupported) {
1628 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1629 }
1630 }
1631
Michael Kolb8872c232013-01-29 10:33:22 -08001632 private void setMeteringAreasIfSupported() {
1633 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001634 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1635 }
1636 }
1637
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001638 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001639 setAutoExposureLockIfSupported();
1640 setAutoWhiteBalanceLockIfSupported();
1641 setFocusAreasIfSupported();
1642 setMeteringAreasIfSupported();
1643
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001644 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07001645 mFocusManager.overrideFocusMode(null);
1646 mParameters.setFocusMode(mFocusManager.getFocusMode());
Andy Huibers10c58162014-03-29 14:06:54 -07001647 SessionStatsCollector.instance().autofocusActive(
1648 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolbd3253f22013-07-12 11:36:47 -07001649
Michael Kolb8872c232013-01-29 10:33:22 -08001650 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001651 updateParametersPictureSize();
1652
1653 // Set JPEG quality.
1654 updateParametersPictureQuality();
1655
1656 // For the following settings, we need to check if the settings are
1657 // still supported by latest driver, if not, ignore the settings.
1658
1659 // Set exposure compensation
1660 updateParametersExposureCompensation();
1661
1662 // Set the scene mode: also sets flash and white balance.
1663 updateParametersSceneMode();
1664
1665 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1666 updateAutoFocusMoveCallback();
1667 }
1668 }
1669
1670 private void updateParametersPictureSize() {
1671 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001672 String pictureSize = settingsManager
1673 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
1674 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001675
Angus Kong63424662014-04-23 10:47:47 -07001676 List<Size> supported = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001677 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
1678 mCameraDevice.getCameraId());
Angus Kong63424662014-04-23 10:47:47 -07001679 Size size = new Size(mParameters.getPictureSize());
Michael Kolb8872c232013-01-29 10:33:22 -08001680
1681 // Set a preview size that is closest to the viewfinder height and has
1682 // the right aspect ratio.
Angus Kong63424662014-04-23 10:47:47 -07001683 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPreviewSizes());
Angus Kongb50b5cb2013-08-09 14:55:20 -07001684 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07001685 (double) size.width() / size.height());
Angus Kong63424662014-04-23 10:47:47 -07001686 Size original = new Size(mParameters.getPreviewSize());
Michael Kolb8872c232013-01-29 10:33:22 -08001687 if (!original.equals(optimalSize)) {
Angus Kong00b7b102014-04-24 15:46:52 -07001688 mParameters.setPreviewSize(optimalSize.width(), optimalSize.height());
Doris Liu6432cd62013-06-13 17:20:31 -07001689
Michael Kolb8872c232013-01-29 10:33:22 -08001690 // Zoom related settings will be changed for different preview
1691 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001692 if (mHandler.getLooper() == Looper.myLooper()) {
1693 // On UI thread only, not when camera starts up
1694 setupPreview();
1695 } else {
1696 mCameraDevice.setParameters(mParameters);
1697 }
Michael Kolb8872c232013-01-29 10:33:22 -08001698 mParameters = mCameraDevice.getParameters();
1699 }
Doris Liu95405742013-11-05 15:25:26 -08001700
Angus Kong00b7b102014-04-24 15:46:52 -07001701 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
1702 mUI.updatePreviewAspectRatio((float) optimalSize.width()
1703 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08001704 }
Angus Kong00b7b102014-04-24 15:46:52 -07001705 Log.i(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001706 }
Michael Kolb8872c232013-01-29 10:33:22 -08001707
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001708 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08001709 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1710 CameraProfile.QUALITY_HIGH);
1711 mParameters.setJpegQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001712 }
Michael Kolb8872c232013-01-29 10:33:22 -08001713
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001714 private void updateParametersExposureCompensation() {
1715 SettingsManager settingsManager = mActivity.getSettingsManager();
Spike Spragueabf54e22014-03-27 15:41:28 -07001716 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
1717 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
Angus Kong88289042014-04-22 16:39:42 -07001718 int max = mCameraCapabilities.getMaxExposureCompensation();
1719 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07001720 if (value >= min && value <= max) {
1721 mParameters.setExposureCompensation(value);
1722 } else {
1723 Log.w(TAG, "invalid exposure range: " + value);
1724 }
Michael Kolb8872c232013-01-29 10:33:22 -08001725 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07001726 // If exposure compensation is not enabled, reset the exposure compensation value.
1727 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08001728 }
Spike Spragueabf54e22014-03-27 15:41:28 -07001729
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001730 }
1731
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001732 private void updateParametersSceneMode() {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001733 SettingsManager settingsManager = mActivity.getSettingsManager();
1734
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001735 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
Angus Kong88289042014-04-22 16:39:42 -07001736 if (mCameraCapabilities
1737 .supports(mCameraCapabilities.getStringifier().sceneModeFromString(mSceneMode))) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001738 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1739 mParameters.setSceneMode(mSceneMode);
1740
1741 // Setting scene mode will change the settings of flash mode,
1742 // white balance, and focus mode. Here we read back the
1743 // parameters, so we can know those settings.
1744 mCameraDevice.setParameters(mParameters);
1745 mParameters = mCameraDevice.getParameters();
1746 }
1747 } else {
1748 mSceneMode = mParameters.getSceneMode();
1749 if (mSceneMode == null) {
1750 mSceneMode = Parameters.SCENE_MODE_AUTO;
1751 }
1752 }
1753
Michael Kolb8872c232013-01-29 10:33:22 -08001754 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1755 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001756 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001757
Michael Kolb8872c232013-01-29 10:33:22 -08001758 // Set focus mode.
1759 mFocusManager.overrideFocusMode(null);
1760 mParameters.setFocusMode(mFocusManager.getFocusMode());
1761 } else {
1762 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1763 }
Michael Kolb8872c232013-01-29 10:33:22 -08001764 }
1765
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001766 private void updateParametersFlashMode() {
1767 SettingsManager settingsManager = mActivity.getSettingsManager();
1768
1769 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
Angus Kong88289042014-04-22 16:39:42 -07001770 if (mCameraCapabilities
1771 .supports(mCameraCapabilities.getStringifier().flashModeFromString(flashMode))) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001772 mParameters.setFlashMode(flashMode);
1773 }
1774 }
1775
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001776 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001777 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001778 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07001779 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07001780 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001781 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07001782 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001783 }
1784 }
1785
Spike Spragueabf54e22014-03-27 15:41:28 -07001786 /**
1787 * Sets the exposure compensation to the given value and also updates settings.
1788 *
1789 * @param value exposure compensation value to be set
1790 */
1791 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07001792 int max = mCameraCapabilities.getMaxExposureCompensation();
1793 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07001794 if (value >= min && value <= max) {
1795 mParameters.setExposureCompensation(value);
1796 SettingsManager settingsManager = mActivity.getSettingsManager();
1797 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
1798 } else {
1799 Log.w(TAG, "invalid exposure range: " + value);
1800 }
1801 }
1802
Michael Kolb8872c232013-01-29 10:33:22 -08001803 // We separate the parameters into several subsets, so we can update only
1804 // the subsets actually need updating. The PREFERENCE set needs extra
1805 // locking because the preference can be changed from GLThread as well.
1806 private void setCameraParameters(int updateSet) {
1807 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1808 updateCameraParametersInitialize();
1809 }
1810
1811 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1812 updateCameraParametersZoom();
1813 }
1814
1815 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001816 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08001817 }
1818
1819 mCameraDevice.setParameters(mParameters);
1820 }
1821
1822 // If the Camera is idle, update the parameters immediately, otherwise
1823 // accumulate them in mUpdateSet and update later.
1824 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1825 mUpdateSet |= additionalUpdateSet;
1826 if (mCameraDevice == null) {
1827 // We will update all the parameters when we open the device, so
1828 // we don't need to do anything now.
1829 mUpdateSet = 0;
1830 return;
1831 } else if (isCameraIdle()) {
1832 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001833 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001834 mUpdateSet = 0;
1835 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08001836 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1837 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08001838 }
1839 }
1840 }
1841
ztenghui7b265a62013-09-09 14:58:44 -07001842 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001843 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001844 return (mCameraState == IDLE) ||
1845 (mCameraState == PREVIEW_STOPPED) ||
1846 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001847 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08001848 }
1849
ztenghui7b265a62013-09-09 14:58:44 -07001850 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001851 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001852 String action = mActivity.getIntent().getAction();
1853 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001854 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08001855 }
1856
1857 private void setupCaptureParams() {
1858 Bundle myExtras = mActivity.getIntent().getExtras();
1859 if (myExtras != null) {
1860 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1861 mCropValue = myExtras.getString("crop");
1862 }
1863 }
1864
Michael Kolb8872c232013-01-29 10:33:22 -08001865 private void initializeCapabilities() {
1866 mInitialParams = mCameraDevice.getParameters();
Angus Kong88289042014-04-22 16:39:42 -07001867 mCameraCapabilities = mCameraDevice.getCapabilities();
1868 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
1869 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
1870 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
1871 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kongdcccc512013-08-08 17:06:03 -07001872 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07001873 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08001874 }
1875
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001876 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08001877 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001878 public int onZoomChanged(int index) {
1879 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001880 if (mPaused) {
1881 return index;
1882 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001883 mZoomValue = index;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001884 if (mParameters == null || mCameraDevice == null) {
1885 return index;
1886 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001887 // Set zoom parameters asynchronously
1888 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07001889 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08001890 Parameters p = mCameraDevice.getParameters();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001891 if (p != null) {
1892 return p.getZoom();
1893 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001894 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001895 }
1896
1897 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001898 public int getCameraState() {
1899 return mCameraState;
1900 }
1901
1902 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001903 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07001904 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08001905 }
Angus Kong86d36312013-01-31 18:22:44 -08001906
1907 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001908 public void onLowMemory() {
1909 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08001910 }
Angus Kong0d00a892013-03-26 11:40:40 -07001911
1912 @Override
1913 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1914 }
1915
1916 @Override
1917 public void onSensorChanged(SensorEvent event) {
1918 int type = event.sensor.getType();
1919 float[] data;
1920 if (type == Sensor.TYPE_ACCELEROMETER) {
1921 data = mGData;
1922 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1923 data = mMData;
1924 } else {
1925 // we should not be here.
1926 return;
1927 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001928 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07001929 data[i] = event.values[i];
1930 }
1931 float[] orientation = new float[3];
1932 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
1933 SensorManager.getOrientation(mR, orientation);
1934 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
1935 if (mHeading < 0) {
1936 mHeading += 360;
1937 }
Angus Kong0d00a892013-03-26 11:40:40 -07001938 }
Doris Liu6432cd62013-06-13 17:20:31 -07001939
Ruben Brunkd217ed02013-10-08 23:31:13 -07001940 // For debugging only.
1941 public void setDebugUri(Uri uri) {
1942 mDebugUri = uri;
1943 }
1944
1945 // For debugging only.
1946 private void saveToDebugUri(byte[] data) {
1947 if (mDebugUri != null) {
1948 OutputStream outputStream = null;
1949 try {
1950 outputStream = mContentResolver.openOutputStream(mDebugUri);
1951 outputStream.write(data);
1952 outputStream.close();
1953 } catch (IOException e) {
1954 Log.e(TAG, "Exception while writing debug jpeg file", e);
1955 } finally {
1956 CameraUtil.closeSilently(outputStream);
1957 }
1958 }
1959 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001960
1961 @Override
1962 public void onRemoteShutterPress() {
1963 capture();
1964 }
Michael Kolb8872c232013-01-29 10:33:22 -08001965}