blob: f06ab934ecc12095ab45cf22a13bb75f32dce61f [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;
Doris Liudb8f9752014-05-12 15:25:13 -070024import android.content.res.Resources;
Michael Kolb8872c232013-01-29 10:33:22 -080025import android.graphics.Bitmap;
Angus Kong454d63f2014-05-06 14:45:59 -070026import android.graphics.BitmapFactory;
Michael Kolb8872c232013-01-29 10:33:22 -080027import android.graphics.SurfaceTexture;
28import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080029import android.hardware.Camera.Parameters;
Angus Kong0d00a892013-03-26 11:40:40 -070030import android.hardware.Sensor;
31import android.hardware.SensorEvent;
32import android.hardware.SensorEventListener;
33import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080034import android.location.Location;
35import android.media.CameraProfile;
36import android.net.Uri;
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -070037import android.os.AsyncTask;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070038import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080039import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080040import android.os.Handler;
41import android.os.Looper;
42import android.os.Message;
43import android.os.MessageQueue;
44import android.os.SystemClock;
45import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080046import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080047import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080048import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080049
Sameer Padala2c8cc452013-11-05 18:49:12 -080050import com.android.camera.PhotoModule.NamedImages.NamedEntity;
51import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080052import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070053import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070054import com.android.camera.app.LocationManager;
55import com.android.camera.app.MediaSaver;
56import com.android.camera.app.MemoryManager;
57import com.android.camera.app.MemoryManager.MemoryListener;
Angus Kong88289042014-04-22 16:39:42 -070058import com.android.camera.cameradevice.CameraCapabilities;
Angus Kong1045fef2014-04-21 11:17:37 -070059import com.android.camera.cameradevice.CameraManager.CameraAFCallback;
60import com.android.camera.cameradevice.CameraManager.CameraAFMoveCallback;
61import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
62import com.android.camera.cameradevice.CameraManager.CameraProxy;
63import com.android.camera.cameradevice.CameraManager.CameraShutterCallback;
Angus Kong2bca2102014-03-11 16:27:30 -070064import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070065import com.android.camera.exif.ExifInterface;
66import com.android.camera.exif.ExifTag;
67import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080068import com.android.camera.hardware.HardwareSpec;
69import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080070import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080071import com.android.camera.remote.RemoteCameraModule;
Angus Kong454d63f2014-05-06 14:45:59 -070072import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080073import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010074import com.android.camera.settings.SettingsUtil;
Angus Kongdcccc512013-08-08 17:06:03 -070075import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070076import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070077import com.android.camera.util.GcamHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070078import com.android.camera.util.SessionStatsCollector;
Angus Kong63424662014-04-23 10:47:47 -070079import com.android.camera.util.Size;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070080import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070081import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070082import com.android.camera2.R;
Seth Raphael5e09d012013-12-18 13:45:03 -080083import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080084
Angus Kong454d63f2014-05-06 14:45:59 -070085import java.io.ByteArrayOutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070086import java.io.File;
87import java.io.FileNotFoundException;
88import java.io.FileOutputStream;
89import java.io.IOException;
90import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010091import java.lang.ref.WeakReference;
Angus Kongdcccc512013-08-08 17:06:03 -070092import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070093import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070094
Michael Kolb8872c232013-01-29 10:33:22 -080095public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080096 extends CameraModule
97 implements PhotoController,
98 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -080099 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800100 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -0800101 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800102 SettingsManager.OnSettingChangedListener,
103 RemoteCameraModule {
Michael Kolb8872c232013-01-29 10:33:22 -0800104
Angus Kong2bca2102014-03-11 16:27:30 -0700105 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
Michael Kolb8872c232013-01-29 10:33:22 -0800106
107 // We number the request code from 1000 to avoid collision with Gallery.
108 private static final int REQUEST_CROP = 1000;
109
Angus Kong13e87c42013-11-25 10:02:47 -0800110 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800111 private static final int MSG_FIRST_TIME_INIT = 1;
112 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800113
114 // The subset of parameters we need to update in setCameraParameters().
115 private static final int UPDATE_PARAM_INITIALIZE = 1;
116 private static final int UPDATE_PARAM_ZOOM = 2;
117 private static final int UPDATE_PARAM_PREFERENCE = 4;
118 private static final int UPDATE_PARAM_ALL = -1;
119
Andy Huibersdef975d2013-11-22 09:13:39 -0800120 // This is the delay before we execute onResume tasks when coming
121 // from the lock screen, to allow time for onPause to execute.
122 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800123
Ruben Brunkd7488272013-10-10 18:45:53 -0700124 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
125
Michael Kolb8872c232013-01-29 10:33:22 -0800126 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800127 private CameraProxy mCameraDevice;
128 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700129 private CameraCapabilities mCameraCapabilities;
Michael Kolb8872c232013-01-29 10:33:22 -0800130 private Parameters mParameters;
131 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800132
133 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800134
Michael Kolb8872c232013-01-29 10:33:22 -0800135 // The activity is going to switch to the specified camera id. This is
136 // needed because texture copy is done in GL thread. -1 means camera is not
137 // switching.
138 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800139
140 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
141 // needed to be updated in mUpdateSet.
142 private int mUpdateSet;
143
144 private static final int SCREEN_DELAY = 2 * 60 * 1000;
145
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800146 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800147
148 private Parameters mInitialParams;
149 private boolean mFocusAreaSupported;
150 private boolean mMeteringAreaSupported;
151 private boolean mAeLockSupported;
152 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700153 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800154
155 // The degrees of the device rotated clockwise from its natural orientation.
156 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800157
158 private static final String sTempCropFilename = "crop-temp";
159
Michael Kolb8872c232013-01-29 10:33:22 -0800160 private boolean mFaceDetectionStarted = false;
161
Michael Kolb8872c232013-01-29 10:33:22 -0800162 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
163 private String mCropValue;
164 private Uri mSaveUri;
165
Ruben Brunkd217ed02013-10-08 23:31:13 -0700166 private Uri mDebugUri;
167
Angus Kongce5480e2013-01-29 17:43:48 -0800168 // We use a queue to generated names of the images to be used later
169 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800170 private NamedImages mNamedImages;
171
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800172 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800173 @Override
174 public void run() {
175 onShutterButtonClick();
176 }
177 };
178
Michael Kolb8872c232013-01-29 10:33:22 -0800179 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800180 * An unpublished intent flag requesting to return as soon as capturing is
181 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800182 */
183 private static final String EXTRA_QUICK_CAPTURE =
184 "android.intent.extra.quickCapture";
185
186 // The display rotation in degrees. This is only valid when mCameraState is
187 // not PREVIEW_STOPPED.
188 private int mDisplayRotation;
189 // The value for android.hardware.Camera.setDisplayOrientation.
190 private int mCameraDisplayOrientation;
191 // The value for UI components like indicators.
192 private int mDisplayOrientation;
193 // The value for android.hardware.Camera.Parameters.setRotation.
194 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700195 // Indicates whether we are using front camera
196 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800197 private boolean mFirstTimeInitialized;
198 private boolean mIsImageCaptureIntent;
199
Michael Kolb8872c232013-01-29 10:33:22 -0800200 private int mCameraState = PREVIEW_STOPPED;
201 private boolean mSnapshotOnIdle = false;
202
203 private ContentResolver mContentResolver;
204
205 private LocationManager mLocationManager;
Doris Liu2b906b82013-12-10 16:34:08 -0800206 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800207
Michael Kolb8872c232013-01-29 10:33:22 -0800208 private final PostViewPictureCallback mPostViewPictureCallback =
209 new PostViewPictureCallback();
210 private final RawPictureCallback mRawPictureCallback =
211 new RawPictureCallback();
212 private final AutoFocusCallback mAutoFocusCallback =
213 new AutoFocusCallback();
214 private final Object mAutoFocusMoveCallback =
215 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700216 ? new AutoFocusMoveCallback()
217 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800218
219 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
220
221 private long mFocusStartTime;
222 private long mShutterCallbackTime;
223 private long mPostViewPictureCallbackTime;
224 private long mRawPictureCallbackTime;
225 private long mJpegPictureCallbackTime;
226 private long mOnResumeTime;
227 private byte[] mJpegImageData;
228
229 // These latency time are for the CameraLatency test.
230 public long mAutoFocusTime;
231 public long mShutterLag;
232 public long mShutterToPictureDisplayedTime;
233 public long mPictureDisplayedToJpegCallbackTime;
234 public long mJpegCallbackFinishTime;
235 public long mCaptureStartTime;
236
237 // This handles everything about focus.
238 private FocusOverlayManager mFocusManager;
239
Doris Liubd1b8f92014-01-03 17:59:51 -0800240 private final int mGcamModeIndex;
Doris Liubd1b8f92014-01-03 17:59:51 -0800241
Michael Kolb8872c232013-01-29 10:33:22 -0800242 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800243
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100244 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800245
Michael Kolb8872c232013-01-29 10:33:22 -0800246 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700247 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800248 private final float[] mGData = new float[3];
249 private final float[] mMData = new float[3];
250 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700251 private int mHeading = -1;
252
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800253 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700254 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700255
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800256 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800257 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800258 @Override
259 public void onMediaSaved(Uri uri) {
260 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700261 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800262 }
263 }
264 };
Angus Kong454d63f2014-05-06 14:45:59 -0700265 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800266
Angus Kong0b9eb5b2014-04-30 15:03:33 -0700267 private final Runnable mResumeTaskRunnable = new Runnable() {
268 @Override
269 public void run() {
270 onResumeTasks();
271 }
272 };
273
Doris Liudb8f9752014-05-12 15:25:13 -0700274 /**
275 * This callback gets called when user select whether or not to
276 * turn on geo-tagging.
277 */
278 public interface LocationDialogCallback {
279 /**
280 * Gets called after user selected/unselected geo-tagging feature.
281 *
282 * @param selected whether or not geo-tagging feature is selected
283 */
284 public void onLocationTaggingSelected(boolean selected);
285 }
286
287 /**
288 * This callback defines the text that is shown in the aspect ratio selection
289 * dialog, provides the current aspect ratio, and gets notified when user changes
290 * aspect ratio selection in the dialog.
291 */
292 public interface AspectRatioDialogCallback {
293 /**
294 * Returns text to show for 4:3 aspect ratio.
295 */
296 public String get4x3AspectRatioText();
297
298 /**
299 * Returns text to show for 16:9 aspect ratio.
300 */
301 public String get16x9AspectRatioText();
302
303 /**
304 * Returns current aspect ratio that is being used to set as default.
305 */
306 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
307
308 /**
309 * Gets notified when user has made the aspect ratio selection.
310 *
311 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700312 * @param dialogHandlingFinishedRunnable runnable to run when the operations
313 * needed to handle changes from dialog
314 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700315 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700316 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
317 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700318 }
319
Angus Kongdcccc512013-08-08 17:06:03 -0700320 private void checkDisplayRotation() {
321 // Set the display orientation if display rotation has changed.
322 // Sometimes this happens when the device is held upside
323 // down and camera app is opened. Rotation animation will
324 // take some time and the rotation value we have got may be
325 // wrong. Framework does not have a callback for this now.
326 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
327 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800328 }
Angus Kongdcccc512013-08-08 17:06:03 -0700329 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
330 mHandler.postDelayed(new Runnable() {
331 @Override
332 public void run() {
333 checkDisplayRotation();
334 }
335 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800336 }
337 }
338
339 /**
340 * This Handler is used to post message back onto the main thread of the
341 * application
342 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100343 private static class MainHandler extends Handler {
344 private final WeakReference<PhotoModule> mModule;
345
346 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800347 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100348 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800349 }
350
Michael Kolb8872c232013-01-29 10:33:22 -0800351 @Override
352 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100353 PhotoModule module = mModule.get();
354 if (module == null) {
355 return;
356 }
Michael Kolb8872c232013-01-29 10:33:22 -0800357 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800358 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100359 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800360 break;
361 }
362
Angus Kong13e87c42013-11-25 10:02:47 -0800363 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100364 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800365 break;
366 }
Michael Kolb8872c232013-01-29 10:33:22 -0800367 }
368 }
369 }
370
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700371 private void switchToGcamCapture() {
372 if (mActivity != null && mGcamModeIndex != 0) {
373 SettingsManager settingsManager = mActivity.getSettingsManager();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700374 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700375 SettingsManager.VALUE_ON);
376
377 // Disable the HDR+ button to prevent callbacks from being
378 // queued before the correct callback is attached to the button
379 // in the new module. The new module will set the enabled/disabled
380 // of this button when the module's preferred camera becomes available.
381 ButtonManager buttonManager = mActivity.getButtonManager();
382 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
383
384 // Do not post this to avoid this module switch getting interleaved with
385 // other button callbacks.
386 mActivity.onModeSelected(mGcamModeIndex);
387 }
388 }
389
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800390 /**
391 * Constructs a new photo module.
392 */
Angus Kongc4e66562013-11-22 23:03:21 -0800393 public PhotoModule(AppController app) {
394 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800395 mGcamModeIndex = app.getAndroidContext().getResources()
396 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800397 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700398
Michael Kolb8872c232013-01-29 10:33:22 -0800399 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100400 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
401 mActivity = activity;
402 // TODO: Need to look at the controller interface to see if we can get
403 // rid of passing in the activity directly.
404 mAppController = mActivity;
405
406 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
407 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800408
409 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800410 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800411
Doris Liudb8f9752014-05-12 15:25:13 -0700412 // TODO: Move this to SettingsManager as a part of upgrade procedure.
413 if (!settingsManager.getBoolean(SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO)) {
414 // Switch to back camera to set aspect ratio.
415 mCameraId = Integer.parseInt(settingsManager
416 .getDefaultCameraIdSetting(activity).getDefault());
417 }
418
Michael Kolb8872c232013-01-29 10:33:22 -0800419 mContentResolver = mActivity.getContentResolver();
420
Michael Kolb8872c232013-01-29 10:33:22 -0800421 // Surface texture is from camera screen nail and startPreview needs it.
422 // This must be done before startPreview.
423 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800424
Angus Kong20fad242013-11-11 18:23:46 -0800425 mActivity.getCameraProvider().requestCamera(mCameraId);
426
Michael Kolb8872c232013-01-29 10:33:22 -0800427 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800428 mLocationManager = mActivity.getLocationManager();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800429 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800430 }
431
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800432 @Override
433 public boolean isUsingBottomBar() {
434 return true;
435 }
436
Michael Kolbd6954f32013-03-08 20:43:01 -0800437 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800438 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800439 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800440 setupCaptureParams();
441 }
442 }
443
444 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800445 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800446 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800447 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700448 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800449 }
450
Doris Liudb8f9752014-05-12 15:25:13 -0700451 /**
452 * Prompt the user to pick to record location and choose aspect ratio for the
453 * very first run of camera only.
454 */
455 private void settingsFirstRun() {
456 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100457
Doris Liudb8f9752014-05-12 15:25:13 -0700458 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800459 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800460 }
Doris Liudb8f9752014-05-12 15:25:13 -0700461
462 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION);
463 boolean aspectRatioPrompt = !settingsManager.getBoolean(
464 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO);
465 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800466 return;
467 }
Doris Liudb8f9752014-05-12 15:25:13 -0700468
Michael Kolb8872c232013-01-29 10:33:22 -0800469 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800470 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800471 if (backCameraId == -1) {
472 // If there is no back camera, do not show the prompt.
473 return;
474 }
Doris Liudb8f9752014-05-12 15:25:13 -0700475
476 if (locationPrompt) {
477 // Show both location and aspect ratio selection dialog.
478 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
479 public void onLocationTaggingSelected(boolean selected) {
480 settingsManager.setLocation(selected, mActivity.getLocationManager());
481 }
482 }, createAspectRatioDialogCallback());
483 } else {
484 // App upgrade. Only show aspect ratio selection.
485 mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
486 }
487 }
488
489 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
490 Size currentSize = new Size(mParameters.getPictureSize());
491 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
492 if (aspectRatio < 1f) {
493 aspectRatio = 1 / aspectRatio;
494 }
495 final AspectRatioSelector.AspectRatio currentAspectRatio;
496 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
497 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
498 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
499 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
500 } else {
501 // TODO: Log error and not show dialog.
502 return null;
503 }
504
505 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
506 List<Size> pictureSizes = ResolutionUtil
507 .getDisplayableSizesFromSupported(sizes, true);
508
509 // This logic below finds the largest resolution for each aspect ratio.
510 // TODO: Move this somewhere that can be shared with SettingsActivity
511 int aspectRatio4x3Resolution = 0;
512 int aspectRatio16x9Resolution = 0;
513 Size largestSize4x3 = new Size(0, 0);
514 Size largestSize16x9 = new Size(0, 0);
515 for (Size size : pictureSizes) {
516 float pictureAspectRatio = (float) size.width() / (float) size.height();
517 pictureAspectRatio = pictureAspectRatio < 1 ?
518 1f / pictureAspectRatio : pictureAspectRatio;
519 int resolution = size.width() * size.height();
520 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
521 if (resolution > aspectRatio4x3Resolution) {
522 aspectRatio4x3Resolution = resolution;
523 largestSize4x3 = size;
524 }
525 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
526 if (resolution > aspectRatio16x9Resolution) {
527 aspectRatio16x9Resolution = resolution;
528 largestSize16x9 = size;
529 }
530 }
531 }
532 aspectRatio16x9Resolution /= 1000000;
533 aspectRatio4x3Resolution /= 1000000;
534
Doris Liu08b0cdd2014-05-13 19:19:55 -0700535 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
536 final Size size4x3ToSelect = largestSize4x3;
537 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700538
539 Resources res = mAppController.getAndroidContext().getResources();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700540 final String aspectRatio4x3Text = res.getString(
541 R.string.megapixel_text_for_4x3_aspect_ratio, aspectRatio4x3Resolution);
542 final String aspectRatio16x9Text = res.getString(
543 R.string.megapixel_text_for_16x9_aspect_ratio, aspectRatio16x9Resolution);
Doris Liudb8f9752014-05-12 15:25:13 -0700544
545 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
546 @Override
547 public String get4x3AspectRatioText() {
548 return aspectRatio4x3Text;
549 }
550
551 @Override
552 public String get16x9AspectRatioText() {
553 return aspectRatio16x9Text;
554 }
555
556 @Override
557 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
558 return currentAspectRatio;
559 }
560
561 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700562 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
563 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700564 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700565 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700566 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
567 largestSize4x3Text);
568 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700569 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700570 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
571 largestSize16x9Text);
572 }
573 mActivity.getSettingsManager().setBoolean(
574 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO, true);
575 if (newAspectRatio != currentAspectRatio) {
Doris Liudb8f9752014-05-12 15:25:13 -0700576 stopPreview();
577 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700578 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
579 } else {
580 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700581 }
582 }
583 };
584 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700585 }
Michael Kolb8872c232013-01-29 10:33:22 -0800586
ztenghui7b265a62013-09-09 14:58:44 -0700587 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700588 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800589 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700590 }
591
592 @Override
593 public void onPreviewUIDestroyed() {
594 if (mCameraDevice == null) {
595 return;
596 }
597 mCameraDevice.setPreviewTexture(null);
598 stopPreview();
599 }
600
Doris Liu1dfe7822013-12-12 00:02:08 -0800601 @Override
602 public void startPreCaptureAnimation() {
603 mAppController.startPreCaptureAnimation();
604 }
605
Michael Kolbd6954f32013-03-08 20:43:01 -0800606 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800607 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800608 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800609 }
610
Michael Kolbd6954f32013-03-08 20:43:01 -0800611 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800612 if (mPaused) {
613 return;
614 }
Erin Dahlgren357b7672013-11-20 17:38:14 -0800615 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800616
Alan Newbergerd41766f2014-04-09 18:25:34 -0700617 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800618 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800619 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800620 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800621 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800622 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800623 if (mFocusManager != null) {
624 mFocusManager.removeMessages();
625 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800626
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700627 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700628 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700629 // Start switch camera animation. Post a message because
630 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800631 }
632
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800633 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100634 new ButtonManager.ButtonCallback() {
635 @Override
636 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800637 // At the time this callback is fired, the camera id
638 // has be set to the desired camera.
639
Angus Kong97e282a2014-03-04 18:44:49 -0800640 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100641 return;
642 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800643 // If switching to back camera, and HDR+ is still on,
644 // switch back to gcam, otherwise handle callback normally.
645 SettingsManager settingsManager = mActivity.getSettingsManager();
646 if (settingsManager.isCameraBackFacing()) {
647 if (settingsManager.requestsReturnToHdrPlus()) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700648 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800649 return;
650 }
651 }
652
Sascha Haeberlingde303232014-02-07 02:30:53 +0100653 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800654
Alan Newbergerd41766f2014-04-09 18:25:34 -0700655 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100656 // We need to keep a preview frame for the animation before
657 // releasing the camera. This will trigger
658 // onPreviewTextureCopied.
659 // TODO: Need to animate the camera switch
660 switchCamera();
661 }
662 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800663
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800664 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100665 new ButtonManager.ButtonCallback() {
666 @Override
667 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700668 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100669 if (GcamHelper.hasGcamCapture()) {
670 // Set the camera setting to default backfacing.
Sascha Haeberlingde303232014-02-07 02:30:53 +0100671 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700672 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100673 } else {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700674 if (settingsManager.isHdrOn()) {
675 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
676 CameraUtil.SCENE_MODE_HDR);
677 } else {
678 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
679 Parameters.SCENE_MODE_AUTO);
680 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100681 updateParametersSceneMode();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700682 mCameraDevice.setParameters(mParameters);
683 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100684 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800685 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100686 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800687
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800688 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
689 @Override
690 public void onClick(View v) {
691 onCaptureCancelled();
692 }
693 };
694
695 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
696 @Override
697 public void onClick(View v) {
698 onCaptureDone();
699 }
700 };
701
702 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
703 @Override
704 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800705 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800706 onCaptureRetake();
707 }
708 };
709
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800710 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700711 public void hardResetSettings(SettingsManager settingsManager) {
712 // PhotoModule should hard reset HDR+ to off.
713 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
714 }
715
716 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800717 public HardwareSpec getHardwareSpec() {
Erin Dahlgren49ab9222014-01-28 17:40:28 -0800718 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800719 }
720
721 @Override
722 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
723 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
724
725 bottomBarSpec.enableCamera = true;
726 bottomBarSpec.cameraCallback = mCameraCallback;
727 bottomBarSpec.enableFlash = true;
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800728 bottomBarSpec.enableHdr = true;
729 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800730 bottomBarSpec.enableGridLines = true;
Spike Spragueabf54e22014-03-27 15:41:28 -0700731 bottomBarSpec.enableExposureCompensation = true;
732 bottomBarSpec.exposureCompensationSetCallback =
733 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
734 @Override
735 public void setExposure(int value) {
736 setExposureCompensation(value);
737 }
738 };
Angus Kong88289042014-04-22 16:39:42 -0700739 bottomBarSpec.minExposureCompensation = mCameraCapabilities.getMinExposureCompensation();
740 bottomBarSpec.maxExposureCompensation = mCameraCapabilities.getMaxExposureCompensation();
741 bottomBarSpec.exposureCompensationStep = mCameraCapabilities.getExposureCompensationStep();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800742
743 if (isImageCaptureIntent()) {
744 bottomBarSpec.showCancel = true;
745 bottomBarSpec.cancelCallback = mCancelCallback;
746 bottomBarSpec.showDone = true;
747 bottomBarSpec.doneCallback = mDoneCallback;
748 bottomBarSpec.showRetake = true;
749 bottomBarSpec.retakeCallback = mRetakeCallback;
750 }
751
752 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800753 }
754
Michael Kolbd6954f32013-03-08 20:43:01 -0800755 // either open a new camera or switch cameras
756 private void openCameraCommon() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800757 mUI.onCameraOpened(mParameters);
Angus Kong0fb819b2013-10-08 13:44:19 -0700758 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800759 // Set hdr plus to default: off.
760 SettingsManager settingsManager = mActivity.getSettingsManager();
761 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700762 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800763 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800764 }
765
ztenghui7b265a62013-09-09 14:58:44 -0700766 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800767 public void updatePreviewAspectRatio(float aspectRatio) {
768 mAppController.updatePreviewAspectRatio(aspectRatio);
769 }
770
Michael Kolb8872c232013-01-29 10:33:22 -0800771 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800772 SettingsManager settingsManager = mActivity.getSettingsManager();
773 if (settingsManager == null) {
774 Log.e(TAG, "Settings manager is null!");
775 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800776 }
Spike Spragueabf54e22014-03-27 15:41:28 -0700777 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
Michael Kolb8872c232013-01-29 10:33:22 -0800778 }
779
Michael Kolb8872c232013-01-29 10:33:22 -0800780 // Snapshots can only be taken after this is called. It should be called
781 // once only. We could have done these things in onCreate() but we want to
782 // make preview screen appear as soon as possible.
783 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700784 if (mFirstTimeInitialized || mPaused) {
785 return;
786 }
Michael Kolb8872c232013-01-29 10:33:22 -0800787
Michael Kolbd6954f32013-03-08 20:43:01 -0800788 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800789
Angus Kong86d36312013-01-31 18:22:44 -0800790 // We set the listener only when both service and shutterbutton
791 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800792 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800793
Michael Kolb8872c232013-01-29 10:33:22 -0800794 mNamedImages = new NamedImages();
795
796 mFirstTimeInitialized = true;
797 addIdleHandler();
798
Spike Spraguee6374b72014-04-25 17:24:32 -0700799 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800800 }
801
Michael Kolbd6954f32013-03-08 20:43:01 -0800802 // If the activity is paused and resumed, this method will be called in
803 // onResume.
804 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800805 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800806 mNamedImages = new NamedImages();
807 mUI.initializeSecondTime(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -0800808 }
809
Michael Kolb8872c232013-01-29 10:33:22 -0800810 private void addIdleHandler() {
811 MessageQueue queue = Looper.myQueue();
812 queue.addIdleHandler(new MessageQueue.IdleHandler() {
813 @Override
814 public boolean queueIdle() {
815 Storage.ensureOSXCompatible();
816 return false;
817 }
818 });
819 }
820
Michael Kolb8872c232013-01-29 10:33:22 -0800821 @Override
822 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800823 if (mFaceDetectionStarted) {
824 return;
825 }
Angus Kong88289042014-04-22 16:39:42 -0700826 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800827 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700828 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700829 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800830 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700831 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800832 }
833 }
834
Michael Kolb8872c232013-01-29 10:33:22 -0800835 @Override
836 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800837 if (!mFaceDetectionStarted) {
838 return;
839 }
Angus Kong88289042014-04-22 16:39:42 -0700840 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800841 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700842 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800843 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800844 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700845 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800846 }
847 }
848
Michael Kolb8872c232013-01-29 10:33:22 -0800849 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700850 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700851
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800852 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700853
Sascha Haeberling37f36112013-08-06 14:31:52 -0700854 public ShutterCallback(boolean needsAnimation) {
855 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700856 }
857
Michael Kolb8872c232013-01-29 10:33:22 -0800858 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700859 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800860 mShutterCallbackTime = System.currentTimeMillis();
861 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
862 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700863 if (mNeedsAnimation) {
864 mActivity.runOnUiThread(new Runnable() {
865 @Override
866 public void run() {
867 animateAfterShutter();
868 }
869 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700870 }
Michael Kolb8872c232013-01-29 10:33:22 -0800871 }
872 }
873
Angus Kong9ef99252013-07-18 18:04:19 -0700874 private final class PostViewPictureCallback
875 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800876 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800877 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800878 mPostViewPictureCallbackTime = System.currentTimeMillis();
879 Log.v(TAG, "mShutterToPostViewCallbackTime = "
880 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
881 + "ms");
882 }
883 }
884
Angus Kong9ef99252013-07-18 18:04:19 -0700885 private final class RawPictureCallback
886 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800887 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800888 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800889 mRawPictureCallbackTime = System.currentTimeMillis();
890 Log.v(TAG, "mShutterToRawCallbackTime = "
891 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
892 }
893 }
894
Angus Kong454d63f2014-05-06 14:45:59 -0700895 private static class ResizeBundle {
896 byte[] jpegData;
897 float targetAspectRatio;
898 ExifInterface exif;
899 }
900
901 /**
902 * @return Cropped image if the target aspect ratio is larger than the jpeg
903 * aspect ratio on the long axis. The original jpeg otherwise.
904 */
905 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
906
907 final byte[] jpegData = dataBundle.jpegData;
908 final ExifInterface exif = dataBundle.exif;
909 float targetAspectRatio = dataBundle.targetAspectRatio;
910
911 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
912 int originalWidth = original.getWidth();
913 int originalHeight = original.getHeight();
914 int newWidth;
915 int newHeight;
916
917 if (originalWidth > originalHeight) {
918 newHeight = (int) (originalWidth / targetAspectRatio);
919 newWidth = originalWidth;
920 } else {
921 newWidth = (int) (originalHeight / targetAspectRatio);
922 newHeight = originalHeight;
923 }
924 int xOffset = (originalWidth - newWidth)/2;
925 int yOffset = (originalHeight - newHeight)/2;
926
927 if (xOffset < 0 || yOffset < 0) {
928 return dataBundle;
929 }
930
931 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
932 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
933 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
934
935 ByteArrayOutputStream stream = new ByteArrayOutputStream();
936
937 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
938 dataBundle.jpegData = stream.toByteArray();
939 return dataBundle;
940 }
941
Angus Kong9ef99252013-07-18 18:04:19 -0700942 private final class JpegPictureCallback
943 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800944 Location mLocation;
945
946 public JpegPictureCallback(Location loc) {
947 mLocation = loc;
948 }
949
950 @Override
Angus Kong454d63f2014-05-06 14:45:59 -0700951 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700952 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800953 if (mPaused) {
954 return;
955 }
Doris Liu6432cd62013-06-13 17:20:31 -0700956 if (mIsImageCaptureIntent) {
957 stopPreview();
958 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700959 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700960 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800961 }
962
963 mJpegPictureCallbackTime = System.currentTimeMillis();
964 // If postview callback has arrived, the captured image is displayed
965 // in postview callback. If not, the captured image is displayed in
966 // raw picture callback.
967 if (mPostViewPictureCallbackTime != 0) {
968 mShutterToPictureDisplayedTime =
969 mPostViewPictureCallbackTime - mShutterCallbackTime;
970 mPictureDisplayedToJpegCallbackTime =
971 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
972 } else {
973 mShutterToPictureDisplayedTime =
974 mRawPictureCallbackTime - mShutterCallbackTime;
975 mPictureDisplayedToJpegCallbackTime =
976 mJpegPictureCallbackTime - mRawPictureCallbackTime;
977 }
978 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
979 + mPictureDisplayedToJpegCallbackTime + "ms");
980
Michael Kolb8872c232013-01-29 10:33:22 -0800981 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
982 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700983 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800984 }
985
Angus Kong454d63f2014-05-06 14:45:59 -0700986 long now = System.currentTimeMillis();
987 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
988 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
989 mJpegPictureCallbackTime = 0;
990
991 final ExifInterface exif = Exif.getExif(originalJpegData);
992
993 if (mShouldResizeTo16x9) {
994 final ResizeBundle dataBundle = new ResizeBundle();
995 dataBundle.jpegData = originalJpegData;
996 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
997 dataBundle.exif = exif;
998 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
999
1000 @Override
1001 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1002 return cropJpegDataToAspectRatio(resizeBundles[0]);
1003 }
1004
1005 @Override
1006 protected void onPostExecute(ResizeBundle result) {
1007 saveFinalPhoto(result.jpegData, result.exif, camera);
1008 }
1009 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1010
1011 } else {
1012 saveFinalPhoto(originalJpegData, exif, camera);
1013 }
1014 }
1015
1016 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1017
Doris Liu36e56fb2013-09-11 17:38:08 -07001018 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001019
Andy Huiberse08bc042014-04-11 19:26:47 -07001020 float zoomValue = 0f;
Angus Kong88289042014-04-22 16:39:42 -07001021 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Andy Huiberse08bc042014-04-11 19:26:47 -07001022 int zoomIndex = mParameters.getZoom();
1023 List<Integer> zoomRatios = mParameters.getZoomRatios();
1024 if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
1025 zoomValue = 0.01f * zoomRatios.get(zoomIndex);
1026 }
1027 }
Angus Kong454d63f2014-05-06 14:45:59 -07001028
Andy Huibers6b9743a2014-04-03 23:23:29 -07001029 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
Andy Huibers10c58162014-03-29 14:06:54 -07001030 UsageStatistics.instance().photoCaptureDoneEvent(
1031 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1032 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers6b9743a2014-04-03 23:23:29 -07001033 isCameraFrontFacing(), hdrOn, zoomValue);
Ruben Brunkd217ed02013-10-08 23:31:13 -07001034
Ruben Brunkd7488272013-10-10 18:45:53 -07001035 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001036 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001037 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1038 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001039 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001040 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1041 width = exifWidth;
1042 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001043 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001044 Size s;
1045 s = new Size(mParameters.getPictureSize());
1046 if ((mJpegRotation + orientation) % 180 == 0) {
1047 width = s.width();
1048 height = s.height();
1049 } else {
1050 width = s.height();
1051 height = s.width();
1052 }
Michael Kolb8872c232013-01-29 10:33:22 -08001053 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001054 NamedEntity name = mNamedImages.getNextNameEntity();
1055 String title = (name == null) ? null : name.title;
1056 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001057
1058 // Handle debug mode outputs
1059 if (mDebugUri != null) {
1060 // If using a debug uri, save jpeg there.
1061 saveToDebugUri(jpegData);
1062
1063 // Adjust the title of the debug image shown in mediastore.
1064 if (title != null) {
1065 title = DEBUG_IMAGE_PREFIX + title;
1066 }
1067 }
1068
Michael Kolb8872c232013-01-29 10:33:22 -08001069 if (title == null) {
1070 Log.e(TAG, "Unbalanced name/data pair");
1071 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001072 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001073 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001074 }
Angus Kong0d00a892013-03-26 11:40:40 -07001075 if (mHeading >= 0) {
1076 // heading direction has been updated by the sensor.
1077 ExifTag directionRefTag = exif.buildTag(
1078 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1079 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1080 ExifTag directionTag = exif.buildTag(
1081 ExifInterface.TAG_GPS_IMG_DIRECTION,
1082 new Rational(mHeading, 1));
1083 exif.setTag(directionRefTag);
1084 exif.setTag(directionTag);
1085 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001086 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001087 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001088 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001089 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001090 // Animate capture with real jpeg data instead of a preview
1091 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001092 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001093 } else {
1094 mJpegImageData = jpegData;
1095 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001096 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001097 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001098 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001099 }
1100 }
1101
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001102 // Send the taken photo to remote shutter listeners, if any are
1103 // registered.
1104 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
1105 @Override
1106 public void run() {
1107 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1108 }
1109 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001110
Michael Kolb8872c232013-01-29 10:33:22 -08001111 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001112 // latency. It's true that someone else could write to the SD card
1113 // in the mean time and fill it, but that could have happened
1114 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001115 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001116 }
1117 }
1118
Angus Kong9ef99252013-07-18 18:04:19 -07001119 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001120 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001121 public void onAutoFocus(boolean focused, CameraProxy camera) {
1122 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001123 if (mPaused) {
1124 return;
1125 }
Michael Kolb8872c232013-01-29 10:33:22 -08001126
1127 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001128 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001129 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001130 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001131 }
1132 }
1133
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001134 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001135 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001136 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001137 @Override
1138 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001139 boolean moving, CameraProxy camera) {
1140 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001141 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001142 }
1143 }
1144
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001145 /**
1146 * This class is just a thread-safe queue for name,date holder objects.
1147 */
1148 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001149 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001150
1151 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001152 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001153 }
1154
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001155 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001156 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001157 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001158 r.date = date;
1159 mQueue.add(r);
1160 }
1161
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001162 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001163 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001164 if (!mQueue.isEmpty()) {
1165 return mQueue.remove(0);
1166 }
Michael Kolb8872c232013-01-29 10:33:22 -08001167 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001168 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001169 }
1170
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001171 public static class NamedEntity {
1172 public String title;
1173 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001174 }
1175 }
1176
1177 private void setCameraState(int state) {
1178 mCameraState = state;
1179 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001180 case PREVIEW_STOPPED:
1181 case SNAPSHOT_IN_PROGRESS:
1182 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001183 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001184 break;
1185 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001186 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001187 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001188 }
1189 }
1190
Sascha Haeberling37f36112013-08-06 14:31:52 -07001191 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001192 // Only animate when in full screen capture mode
1193 // i.e. If monkey/a user swipes to the gallery during picture taking,
1194 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001195 if (!mIsImageCaptureIntent) {
1196 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001197 }
Michael Kolb8872c232013-01-29 10:33:22 -08001198 }
1199
1200 @Override
1201 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001202 // If we are already in the middle of taking a snapshot or the image
1203 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001204 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001205 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001206 return false;
1207 }
1208 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001209
Michael Kolb8872c232013-01-29 10:33:22 -08001210 mPostViewPictureCallbackTime = 0;
1211 mJpegImageData = null;
1212
Angus Kongb50b5cb2013-08-09 14:55:20 -07001213 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001214
1215 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001216 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001217 }
1218
1219 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -08001220 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001221
Doris Liu3cf565c2013-02-15 10:55:37 -08001222 // We need to be consistent with the framework orientation (i.e. the
1223 // orientation of the UI.) when the auto-rotate screen setting is on.
1224 if (mActivity.isAutoRotateScreen()) {
1225 orientation = (360 - mDisplayRotation) % 360;
1226 } else {
1227 orientation = mOrientation;
1228 }
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +01001229 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
1230 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001231 mParameters.setRotation(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001232 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001233 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -08001234 mCameraDevice.setParameters(mParameters);
1235
Sascha Haeberling88901942013-08-28 17:49:00 -07001236 // We don't want user to press the button again while taking a
1237 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001238 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001239 mCameraDevice.takePicture(mHandler,
1240 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001241 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001242 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001243
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001244 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001245
1246 mFaceDetectionStarted = false;
1247 setCameraState(SNAPSHOT_IN_PROGRESS);
1248 return true;
1249 }
1250
1251 @Override
1252 public void setFocusParameters() {
1253 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1254 }
1255
Michael Kolbd6954f32013-03-08 20:43:01 -08001256 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001257 // If scene mode is set, we cannot set flash mode, white balance, and
1258 // focus mode, instead, we read it from driver
1259 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001260 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001261 }
1262 }
1263
Doris Liu8ad8ad42014-03-27 14:43:31 -07001264 private void overrideCameraSettings(final String flashMode, final String focusMode) {
Erin Dahlgrene419b192013-12-03 13:10:27 -08001265 SettingsManager settingsManager = mActivity.getSettingsManager();
1266 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
Erin Dahlgrene419b192013-12-03 13:10:27 -08001267 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001268 }
1269
1270 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001271 public void onOrientationChanged(int orientation) {
1272 // We keep the last known orientation. So if the user first orient
1273 // the camera then point the camera to floor or sky, we still have
1274 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001275 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1276 return;
1277 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001278 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001279 }
1280
1281 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001282 public void onCameraAvailable(CameraProxy cameraProxy) {
1283 if (mPaused) {
1284 return;
1285 }
1286 mCameraDevice = cameraProxy;
1287
1288 initializeCapabilities();
1289
1290 // Reset zoom value index.
1291 mZoomValue = 0;
1292 if (mFocusManager == null) {
1293 initializeFocusManager();
1294 }
Angus Kong88289042014-04-22 16:39:42 -07001295 mFocusManager.setParameters(mInitialParams, mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001296
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001297 // Do camera parameter dependent initialization.
Angus Kong20fad242013-11-11 18:23:46 -08001298 mParameters = mCameraDevice.getParameters();
1299 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001300 // Set a listener which updates camera parameters based
1301 // on changed settings.
1302 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001303 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001304 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001305
Angus Kong20fad242013-11-11 18:23:46 -08001306 startPreview();
1307
1308 onCameraOpened();
1309 }
1310
1311 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001312 public void onCaptureCancelled() {
1313 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1314 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001315 }
1316
Michael Kolbd6954f32013-03-08 20:43:01 -08001317 @Override
1318 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001319 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001320 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001321 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001322 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001323 setupPreview();
1324 }
1325
Michael Kolbd6954f32013-03-08 20:43:01 -08001326 @Override
1327 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001328 if (mPaused) {
1329 return;
1330 }
1331
1332 byte[] data = mJpegImageData;
1333
1334 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001335 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001336 // caller specifies a "save uri" then write the data to its
1337 // stream. Otherwise, pass back a scaled down version of the bitmap
1338 // directly in the extras.
1339 if (mSaveUri != null) {
1340 OutputStream outputStream = null;
1341 try {
1342 outputStream = mContentResolver.openOutputStream(mSaveUri);
1343 outputStream.write(data);
1344 outputStream.close();
1345
1346 mActivity.setResultEx(Activity.RESULT_OK);
1347 mActivity.finish();
1348 } catch (IOException ex) {
1349 // ignore exception
1350 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001351 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001352 }
1353 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001354 ExifInterface exif = Exif.getExif(data);
1355 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001356 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1357 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001358 mActivity.setResultEx(Activity.RESULT_OK,
1359 new Intent("inline-data").putExtra("data", bitmap));
1360 mActivity.finish();
1361 }
1362 } else {
1363 // Save the image to a temp file and invoke the cropper
1364 Uri tempUri = null;
1365 FileOutputStream tempStream = null;
1366 try {
1367 File path = mActivity.getFileStreamPath(sTempCropFilename);
1368 path.delete();
1369 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1370 tempStream.write(data);
1371 tempStream.close();
1372 tempUri = Uri.fromFile(path);
1373 } catch (FileNotFoundException ex) {
1374 mActivity.setResultEx(Activity.RESULT_CANCELED);
1375 mActivity.finish();
1376 return;
1377 } catch (IOException ex) {
1378 mActivity.setResultEx(Activity.RESULT_CANCELED);
1379 mActivity.finish();
1380 return;
1381 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001382 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001383 }
1384
1385 Bundle newExtras = new Bundle();
1386 if (mCropValue.equals("circle")) {
1387 newExtras.putString("circleCrop", "true");
1388 }
1389 if (mSaveUri != null) {
1390 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1391 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001392 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001393 }
1394 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001395 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001396 }
1397
Sascha Haeberling37f36112013-08-06 14:31:52 -07001398 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001399 final String CROP_ACTION = "com.android.camera.action.CROP";
1400 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001401
1402 cropIntent.setData(tempUri);
1403 cropIntent.putExtras(newExtras);
1404
1405 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1406 }
1407 }
1408
Michael Kolb8872c232013-01-29 10:33:22 -08001409 @Override
1410 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001411 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001412 }
1413
1414 @Override
1415 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001416 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001417 || (mCameraState == PREVIEW_STOPPED)) {
1418 return;
1419 }
Michael Kolb8872c232013-01-29 10:33:22 -08001420
1421 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001422 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001423 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001424 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001425 return;
1426 }
Alan Newbergerd41766f2014-04-09 18:25:34 -07001427 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
Michael Kolb8872c232013-01-29 10:33:22 -08001428
Angus Kongb50b5cb2013-08-09 14:55:20 -07001429 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001430 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001431 }
Michael Kolb8872c232013-01-29 10:33:22 -08001432 // If the user wants to do a snapshot while the previous one is still
1433 // in progress, remember the fact and do it after we finish the previous
1434 // one and re-start the preview. Snapshot in progress also includes the
1435 // state that autofocus is focusing and a picture will be taken when
1436 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001437 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1438 if (!mIsImageCaptureIntent) {
1439 mSnapshotOnIdle = true;
1440 }
Michael Kolb8872c232013-01-29 10:33:22 -08001441 return;
1442 }
1443
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001444 mSnapshotOnIdle = false;
Angus Kongeaaf56d2014-02-20 11:59:10 -08001445 mFocusManager.focusAndCapture();
Michael Kolb8872c232013-01-29 10:33:22 -08001446 }
1447
Andy Huibersdef975d2013-11-22 09:13:39 -08001448 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001449 if (mPaused) {
1450 return;
1451 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001452 Log.v(TAG, "Executing onResumeTasks.");
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001453 CameraProvider camProvider = mActivity.getCameraProvider();
1454 if (camProvider == null) {
1455 // No camera provider, the Activity is destroyed already.
1456 return;
1457 }
1458 camProvider.requestCamera(mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -08001459
Michael Kolb8872c232013-01-29 10:33:22 -08001460 mJpegPictureCallbackTime = 0;
1461 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001462
1463 mOnResumeTime = SystemClock.uptimeMillis();
1464 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001465
1466 // If first time initialization is not finished, put it in the
1467 // message queue.
1468 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001469 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001470 } else {
1471 initializeSecondTime();
1472 }
Michael Kolb8872c232013-01-29 10:33:22 -08001473
Angus Kong0d00a892013-03-26 11:40:40 -07001474 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1475 if (gsensor != null) {
1476 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1477 }
1478
1479 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1480 if (msensor != null) {
1481 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1482 }
Michael Kolb8872c232013-01-29 10:33:22 -08001483 }
1484
Angus Kongc4e66562013-11-22 23:03:21 -08001485 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001486 * @return Whether the currently active camera is front-facing.
1487 */
1488 private boolean isCameraFrontFacing() {
1489 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1490 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1491 }
1492
1493 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001494 * The focus manager is the first UI related element to get initialized, and
1495 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001496 */
1497 private void initializeFocusManager() {
1498 // Create FocusManager object. startPreview needs it.
1499 // if mFocusManager not null, reuse it
1500 // otherwise create a new instance
1501 if (mFocusManager != null) {
1502 mFocusManager.removeMessages();
1503 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001504 mMirror = isCameraFrontFacing();
Angus Kongc4e66562013-11-22 23:03:21 -08001505 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1506 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001507 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1508 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001509 mInitialParams, this, mMirror,
Doris Liu482de022013-12-18 19:18:16 -08001510 mActivity.getMainLooper(), mUI.getFocusUI());
Angus Kongc4e66562013-11-22 23:03:21 -08001511 }
Doris Liu482de022013-12-18 19:18:16 -08001512 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001513 }
1514
1515 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001516 public void resume() {
1517 mPaused = false;
Doris Liu482de022013-12-18 19:18:16 -08001518 if (mFocusManager != null) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001519 // If camera is not open when resume is called, focus manager will
1520 // not
Doris Liu15b99612013-12-21 11:32:28 -08001521 // be initialized yet, in which case it will start listening to
1522 // preview area size change later in the initialization.
Doris Liu482de022013-12-18 19:18:16 -08001523 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1524 }
Doris Liu59401042014-01-14 17:51:32 -08001525
1526 if (mUI.getPreviewAreaSizeChangedListener() != null) {
1527 mAppController.addPreviewAreaSizeChangedListener(
1528 mUI.getPreviewAreaSizeChangedListener());
1529 }
1530
Angus Kongc4e66562013-11-22 23:03:21 -08001531 // Add delay on resume from lock screen only, in order to to speed up
1532 // the onResume --> onPause --> onResume cycle from lock screen.
1533 // Don't do always because letting go of thread can cause delay.
1534 String action = mActivity.getIntent().getAction();
1535 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1536 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1537 Log.v(TAG, "On resume, from lock screen.");
1538 // Note: onPauseAfterSuper() will delete this runnable, so we will
1539 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001540 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001541 } else {
1542 Log.v(TAG, "On resume.");
1543 onResumeTasks();
1544 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001545 getServices().getRemoteShutterListener().onModuleReady(this);
Andy Huibers10c58162014-03-29 14:06:54 -07001546 SessionStatsCollector.instance().sessionActive(true);
Angus Kongc4e66562013-11-22 23:03:21 -08001547 }
1548
1549 @Override
1550 public void pause() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001551 getServices().getRemoteShutterListener().onModuleExit();
Michael Kolb8872c232013-01-29 10:33:22 -08001552 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001553 mHandler.removeCallbacks(mResumeTaskRunnable);
Andy Huibers10c58162014-03-29 14:06:54 -07001554 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001555
Angus Kong0d00a892013-03-26 11:40:40 -07001556 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1557 if (gsensor != null) {
1558 mSensorManager.unregisterListener(this, gsensor);
1559 }
1560
1561 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1562 if (msensor != null) {
1563 mSensorManager.unregisterListener(this, msensor);
1564 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001565
Michael Kolb8872c232013-01-29 10:33:22 -08001566 // Reset the focus first. Camera CTS does not guarantee that
1567 // cancelAutoFocus is allowed after preview stops.
1568 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1569 mCameraDevice.cancelAutoFocus();
1570 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001571
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001572 // If the camera has not been opened asynchronously yet,
1573 // and startPreview hasn't been called, then this is a no-op.
1574 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001575 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001576
Angus Kongce5480e2013-01-29 17:43:48 -08001577 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001578
Michael Kolb8872c232013-01-29 10:33:22 -08001579 // If we are in an image capture intent and has taken
1580 // a picture, we just clear it in onPause.
1581 mJpegImageData = null;
1582
Angus Kongdcccc512013-08-08 17:06:03 -07001583 // Remove the messages and runnables in the queue.
1584 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001585
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001586 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001587 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001588 mUI.onPause();
1589
Michael Kolb8872c232013-01-29 10:33:22 -08001590 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001591 if (mFocusManager != null) {
1592 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001593 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001594 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001595 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu59401042014-01-14 17:51:32 -08001596 if (mUI.getPreviewAreaSizeChangedListener() != null) {
1597 mAppController.removePreviewAreaSizeChangedListener(
1598 mUI.getPreviewAreaSizeChangedListener());
1599 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001600
1601 SettingsManager settingsManager = mActivity.getSettingsManager();
1602 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001603 }
1604
Angus Kong20fad242013-11-11 18:23:46 -08001605 @Override
1606 public void destroy() {
1607 // TODO: implement this.
1608 }
1609
1610 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001611 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001612 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001613 }
1614
1615 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001616 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001617 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001618 setDisplayOrientation();
1619 }
1620 }
1621
Michael Kolb8872c232013-01-29 10:33:22 -08001622 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001623 return isCameraIdle()
1624 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001625 }
1626
1627 @Override
1628 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001629 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001630 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001631 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001632 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001633 setCameraState(FOCUSING);
1634 }
1635
1636 @Override
1637 public void cancelAutoFocus() {
1638 mCameraDevice.cancelAutoFocus();
1639 setCameraState(IDLE);
1640 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1641 }
1642
Michael Kolb8872c232013-01-29 10:33:22 -08001643 @Override
1644 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001645 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1646 || mCameraState == SNAPSHOT_IN_PROGRESS
1647 || mCameraState == SWITCHING_CAMERA
1648 || mCameraState == PREVIEW_STOPPED) {
1649 return;
1650 }
1651
1652 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001653 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1654 return;
1655 }
Doris Liu482de022013-12-18 19:18:16 -08001656 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001657 }
1658
1659 @Override
1660 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001661 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001662 }
1663
1664 @Override
1665 public boolean onKeyDown(int keyCode, KeyEvent event) {
1666 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001667 case KeyEvent.KEYCODE_VOLUME_UP:
1668 case KeyEvent.KEYCODE_VOLUME_DOWN:
1669 case KeyEvent.KEYCODE_FOCUS:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001670 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001671 if (event.getRepeatCount() == 0) {
1672 onShutterButtonFocus(true);
1673 }
1674 return true;
1675 }
1676 return false;
1677 case KeyEvent.KEYCODE_CAMERA:
1678 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1679 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001680 }
1681 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001682 case KeyEvent.KEYCODE_DPAD_CENTER:
1683 // If we get a dpad center event without any focused view, move
1684 // the focus to the shutter button and press it.
1685 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1686 // Start auto-focus immediately to reduce shutter lag. After
1687 // the shutter button gets the focus, onShutterButtonFocus()
1688 // will be called again but it is fine.
1689 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001690 }
1691 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001692 }
1693 return false;
1694 }
1695
1696 @Override
1697 public boolean onKeyUp(int keyCode, KeyEvent event) {
1698 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001699 case KeyEvent.KEYCODE_VOLUME_UP:
1700 case KeyEvent.KEYCODE_VOLUME_DOWN:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001701 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001702 onShutterButtonClick();
1703 return true;
1704 }
1705 return false;
1706 case KeyEvent.KEYCODE_FOCUS:
1707 if (mFirstTimeInitialized) {
1708 onShutterButtonFocus(false);
1709 }
Michael Kolb8872c232013-01-29 10:33:22 -08001710 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001711 }
1712 return false;
1713 }
1714
Michael Kolb8872c232013-01-29 10:33:22 -08001715 private void closeCamera() {
1716 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001717 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001718 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001719 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001720 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001721
Michael Kolb8872c232013-01-29 10:33:22 -08001722 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001723 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001724 mCameraDevice = null;
1725 setCameraState(PREVIEW_STOPPED);
1726 mFocusManager.onCameraReleased();
1727 }
1728 }
1729
1730 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001731 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1732 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001733 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001734 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001735 if (mFocusManager != null) {
1736 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1737 }
Doris Liu6432cd62013-06-13 17:20:31 -07001738 // Change the camera display orientation
1739 if (mCameraDevice != null) {
1740 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1741 }
Michael Kolb8872c232013-01-29 10:33:22 -08001742 }
1743
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001744 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001745 private void setupPreview() {
1746 mFocusManager.resetTouchFocus();
1747 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001748 }
1749
Angus Kong20fad242013-11-11 18:23:46 -08001750 /**
1751 * Returns whether we can/should start the preview or not.
1752 */
1753 private boolean checkPreviewPreconditions() {
1754 if (mPaused) {
1755 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001756 }
Michael Kolb8872c232013-01-29 10:33:22 -08001757
Angus Kong20fad242013-11-11 18:23:46 -08001758 if (mCameraDevice == null) {
1759 Log.w(TAG, "startPreview: camera device not ready yet.");
1760 return false;
1761 }
1762
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001763 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001764 if (st == null) {
1765 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001766 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001767 }
1768
1769 if (!mCameraPreviewParamsReady) {
1770 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001771 return false;
1772 }
1773 return true;
1774 }
1775
1776 /**
1777 * The start/stop preview should only run on the UI thread.
1778 */
1779 private void startPreview() {
1780 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001781 return;
1782 }
Angus Kong20fad242013-11-11 18:23:46 -08001783
Angus Kong2bca2102014-03-11 16:27:30 -07001784 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001785 setDisplayOrientation();
1786
1787 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001788 // If the focus mode is continuous autofocus, call cancelAutoFocus
1789 // to resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001790 String focusMode = mFocusManager.getFocusMode();
1791 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001792 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001793 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001794 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1795 }
1796 setCameraParameters(UPDATE_PARAM_ALL);
1797 // Let UI set its expected aspect ratio
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001798 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001799
Alan Newbergerd41766f2014-04-09 18:25:34 -07001800 Log.i(TAG, "startPreview");
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001801 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001802
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001803 mFocusManager.onPreviewStarted();
1804 onPreviewStarted();
Andy Huibers10c58162014-03-29 14:06:54 -07001805 SessionStatsCollector.instance().previewActive(true);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001806 if (mSnapshotOnIdle) {
1807 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001808 }
1809 }
1810
Michael Kolbd6954f32013-03-08 20:43:01 -08001811 @Override
1812 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001813 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001814 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001815 mCameraDevice.stopPreview();
1816 mFaceDetectionStarted = false;
1817 }
1818 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001819 if (mFocusManager != null) {
1820 mFocusManager.onPreviewStopped();
1821 }
Andy Huibers10c58162014-03-29 14:06:54 -07001822 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001823 }
1824
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001825 @Override
Erin Dahlgren1648c362014-01-06 15:06:04 -08001826 public void onSettingChanged(SettingsManager settingsManager, int id) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001827 switch (id) {
1828 case SettingsManager.SETTING_FLASH_MODE: {
1829 updateParametersFlashMode();
1830 break;
1831 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001832 default: {
1833 // Do nothing.
1834 }
1835 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001836
1837 if (mCameraDevice != null) {
1838 mCameraDevice.setParameters(mParameters);
1839 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001840 }
1841
Michael Kolb8872c232013-01-29 10:33:22 -08001842 private void updateCameraParametersInitialize() {
1843 // Reset preview frame rate to the maximum because it may be lowered by
1844 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07001845 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07001846 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001847 mParameters.setPreviewFpsRange(
1848 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1849 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001850 }
1851
Angus Kongb50b5cb2013-08-09 14:55:20 -07001852 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001853
1854 // Disable video stabilization. Convenience methods not available in API
1855 // level <= 14
1856 String vstabSupported = mParameters.get("video-stabilization-supported");
1857 if ("true".equals(vstabSupported)) {
1858 mParameters.set("video-stabilization", "false");
1859 }
1860 }
1861
1862 private void updateCameraParametersZoom() {
1863 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07001864 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001865 mParameters.setZoom(mZoomValue);
1866 }
1867 }
1868
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001869 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001870 private void setAutoExposureLockIfSupported() {
1871 if (mAeLockSupported) {
1872 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1873 }
1874 }
1875
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001876 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001877 private void setAutoWhiteBalanceLockIfSupported() {
1878 if (mAwbLockSupported) {
1879 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1880 }
1881 }
1882
Michael Kolb8872c232013-01-29 10:33:22 -08001883 private void setFocusAreasIfSupported() {
1884 if (mFocusAreaSupported) {
1885 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1886 }
1887 }
1888
Michael Kolb8872c232013-01-29 10:33:22 -08001889 private void setMeteringAreasIfSupported() {
1890 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001891 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1892 }
1893 }
1894
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001895 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001896 setAutoExposureLockIfSupported();
1897 setAutoWhiteBalanceLockIfSupported();
1898 setFocusAreasIfSupported();
1899 setMeteringAreasIfSupported();
1900
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001901 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07001902 mFocusManager.overrideFocusMode(null);
1903 mParameters.setFocusMode(mFocusManager.getFocusMode());
Andy Huibers10c58162014-03-29 14:06:54 -07001904 SessionStatsCollector.instance().autofocusActive(
1905 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolbd3253f22013-07-12 11:36:47 -07001906
Michael Kolb8872c232013-01-29 10:33:22 -08001907 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001908 updateParametersPictureSize();
1909
1910 // Set JPEG quality.
1911 updateParametersPictureQuality();
1912
1913 // For the following settings, we need to check if the settings are
1914 // still supported by latest driver, if not, ignore the settings.
1915
1916 // Set exposure compensation
1917 updateParametersExposureCompensation();
1918
1919 // Set the scene mode: also sets flash and white balance.
1920 updateParametersSceneMode();
1921
1922 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1923 updateAutoFocusMoveCallback();
1924 }
1925 }
1926
1927 private void updateParametersPictureSize() {
1928 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001929 String pictureSize = settingsManager
1930 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
1931 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001932
Angus Kong63424662014-04-23 10:47:47 -07001933 List<Size> supported = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001934 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
1935 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07001936
1937 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
1938 mCameraDevice.getCameraId());
1939 if (ApiHelper.IS_NEXUS_5) {
1940 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
1941 mShouldResizeTo16x9 = true;
1942 } else {
1943 mShouldResizeTo16x9 = false;
1944 }
1945 }
Michael Kolb8872c232013-01-29 10:33:22 -08001946
1947 // Set a preview size that is closest to the viewfinder height and has
1948 // the right aspect ratio.
Angus Kong63424662014-04-23 10:47:47 -07001949 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPreviewSizes());
Angus Kongb50b5cb2013-08-09 14:55:20 -07001950 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07001951 (double) size.width() / size.height());
Angus Kong63424662014-04-23 10:47:47 -07001952 Size original = new Size(mParameters.getPreviewSize());
Michael Kolb8872c232013-01-29 10:33:22 -08001953 if (!original.equals(optimalSize)) {
Angus Kong00b7b102014-04-24 15:46:52 -07001954 mParameters.setPreviewSize(optimalSize.width(), optimalSize.height());
Doris Liu6432cd62013-06-13 17:20:31 -07001955
Michael Kolb8872c232013-01-29 10:33:22 -08001956 // Zoom related settings will be changed for different preview
1957 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001958 if (mHandler.getLooper() == Looper.myLooper()) {
1959 // On UI thread only, not when camera starts up
1960 setupPreview();
1961 } else {
1962 mCameraDevice.setParameters(mParameters);
1963 }
Michael Kolb8872c232013-01-29 10:33:22 -08001964 mParameters = mCameraDevice.getParameters();
1965 }
Doris Liu95405742013-11-05 15:25:26 -08001966
Angus Kong00b7b102014-04-24 15:46:52 -07001967 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
1968 mUI.updatePreviewAspectRatio((float) optimalSize.width()
1969 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08001970 }
Angus Kong00b7b102014-04-24 15:46:52 -07001971 Log.i(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001972 }
Michael Kolb8872c232013-01-29 10:33:22 -08001973
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001974 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08001975 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1976 CameraProfile.QUALITY_HIGH);
1977 mParameters.setJpegQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001978 }
Michael Kolb8872c232013-01-29 10:33:22 -08001979
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001980 private void updateParametersExposureCompensation() {
1981 SettingsManager settingsManager = mActivity.getSettingsManager();
Spike Spragueabf54e22014-03-27 15:41:28 -07001982 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
1983 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
Angus Kong88289042014-04-22 16:39:42 -07001984 int max = mCameraCapabilities.getMaxExposureCompensation();
1985 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07001986 if (value >= min && value <= max) {
1987 mParameters.setExposureCompensation(value);
1988 } else {
1989 Log.w(TAG, "invalid exposure range: " + value);
1990 }
Michael Kolb8872c232013-01-29 10:33:22 -08001991 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07001992 // If exposure compensation is not enabled, reset the exposure compensation value.
1993 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08001994 }
Spike Spragueabf54e22014-03-27 15:41:28 -07001995
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001996 }
1997
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001998 private void updateParametersSceneMode() {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001999 SettingsManager settingsManager = mActivity.getSettingsManager();
2000
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002001 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
Angus Kong88289042014-04-22 16:39:42 -07002002 if (mCameraCapabilities
2003 .supports(mCameraCapabilities.getStringifier().sceneModeFromString(mSceneMode))) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002004 if (!mParameters.getSceneMode().equals(mSceneMode)) {
2005 mParameters.setSceneMode(mSceneMode);
2006
2007 // Setting scene mode will change the settings of flash mode,
2008 // white balance, and focus mode. Here we read back the
2009 // parameters, so we can know those settings.
2010 mCameraDevice.setParameters(mParameters);
2011 mParameters = mCameraDevice.getParameters();
2012 }
2013 } else {
2014 mSceneMode = mParameters.getSceneMode();
2015 if (mSceneMode == null) {
2016 mSceneMode = Parameters.SCENE_MODE_AUTO;
2017 }
2018 }
2019
Michael Kolb8872c232013-01-29 10:33:22 -08002020 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
2021 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002022 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002023
Michael Kolb8872c232013-01-29 10:33:22 -08002024 // Set focus mode.
2025 mFocusManager.overrideFocusMode(null);
2026 mParameters.setFocusMode(mFocusManager.getFocusMode());
2027 } else {
2028 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
2029 }
Michael Kolb8872c232013-01-29 10:33:22 -08002030 }
2031
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002032 private void updateParametersFlashMode() {
2033 SettingsManager settingsManager = mActivity.getSettingsManager();
2034
2035 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
Angus Kong88289042014-04-22 16:39:42 -07002036 if (mCameraCapabilities
2037 .supports(mCameraCapabilities.getStringifier().flashModeFromString(flashMode))) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002038 mParameters.setFlashMode(flashMode);
2039 }
2040 }
2041
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002042 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002043 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07002044 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07002045 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002046 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002047 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002048 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002049 }
2050 }
2051
Spike Spragueabf54e22014-03-27 15:41:28 -07002052 /**
2053 * Sets the exposure compensation to the given value and also updates settings.
2054 *
2055 * @param value exposure compensation value to be set
2056 */
2057 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002058 int max = mCameraCapabilities.getMaxExposureCompensation();
2059 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002060 if (value >= min && value <= max) {
2061 mParameters.setExposureCompensation(value);
2062 SettingsManager settingsManager = mActivity.getSettingsManager();
2063 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
2064 } else {
2065 Log.w(TAG, "invalid exposure range: " + value);
2066 }
2067 }
2068
Michael Kolb8872c232013-01-29 10:33:22 -08002069 // We separate the parameters into several subsets, so we can update only
2070 // the subsets actually need updating. The PREFERENCE set needs extra
2071 // locking because the preference can be changed from GLThread as well.
2072 private void setCameraParameters(int updateSet) {
2073 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2074 updateCameraParametersInitialize();
2075 }
2076
2077 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2078 updateCameraParametersZoom();
2079 }
2080
2081 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002082 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002083 }
2084
2085 mCameraDevice.setParameters(mParameters);
2086 }
2087
2088 // If the Camera is idle, update the parameters immediately, otherwise
2089 // accumulate them in mUpdateSet and update later.
2090 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2091 mUpdateSet |= additionalUpdateSet;
2092 if (mCameraDevice == null) {
2093 // We will update all the parameters when we open the device, so
2094 // we don't need to do anything now.
2095 mUpdateSet = 0;
2096 return;
2097 } else if (isCameraIdle()) {
2098 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002099 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002100 mUpdateSet = 0;
2101 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002102 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2103 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002104 }
2105 }
2106 }
2107
ztenghui7b265a62013-09-09 14:58:44 -07002108 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002109 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002110 return (mCameraState == IDLE) ||
2111 (mCameraState == PREVIEW_STOPPED) ||
2112 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002113 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002114 }
2115
ztenghui7b265a62013-09-09 14:58:44 -07002116 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002117 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002118 String action = mActivity.getIntent().getAction();
2119 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002120 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002121 }
2122
2123 private void setupCaptureParams() {
2124 Bundle myExtras = mActivity.getIntent().getExtras();
2125 if (myExtras != null) {
2126 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2127 mCropValue = myExtras.getString("crop");
2128 }
2129 }
2130
Michael Kolb8872c232013-01-29 10:33:22 -08002131 private void initializeCapabilities() {
2132 mInitialParams = mCameraDevice.getParameters();
Angus Kong88289042014-04-22 16:39:42 -07002133 mCameraCapabilities = mCameraDevice.getCapabilities();
2134 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2135 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2136 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2137 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kongdcccc512013-08-08 17:06:03 -07002138 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07002139 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002140 }
2141
Marco Nelissen0744e4a2013-11-22 01:47:37 +00002142 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08002143 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002144 public int onZoomChanged(int index) {
2145 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002146 if (mPaused) {
2147 return index;
2148 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002149 mZoomValue = index;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002150 if (mParameters == null || mCameraDevice == null) {
2151 return index;
2152 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002153 // Set zoom parameters asynchronously
2154 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07002155 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08002156 Parameters p = mCameraDevice.getParameters();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002157 if (p != null) {
2158 return p.getZoom();
2159 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002160 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002161 }
2162
2163 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002164 public int getCameraState() {
2165 return mCameraState;
2166 }
2167
2168 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002169 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002170 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002171 }
Angus Kong86d36312013-01-31 18:22:44 -08002172
2173 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002174 public void onLowMemory() {
2175 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002176 }
Angus Kong0d00a892013-03-26 11:40:40 -07002177
2178 @Override
2179 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2180 }
2181
2182 @Override
2183 public void onSensorChanged(SensorEvent event) {
2184 int type = event.sensor.getType();
2185 float[] data;
2186 if (type == Sensor.TYPE_ACCELEROMETER) {
2187 data = mGData;
2188 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2189 data = mMData;
2190 } else {
2191 // we should not be here.
2192 return;
2193 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002194 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002195 data[i] = event.values[i];
2196 }
2197 float[] orientation = new float[3];
2198 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2199 SensorManager.getOrientation(mR, orientation);
2200 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2201 if (mHeading < 0) {
2202 mHeading += 360;
2203 }
Angus Kong0d00a892013-03-26 11:40:40 -07002204 }
Doris Liu6432cd62013-06-13 17:20:31 -07002205
Ruben Brunkd217ed02013-10-08 23:31:13 -07002206 // For debugging only.
2207 public void setDebugUri(Uri uri) {
2208 mDebugUri = uri;
2209 }
2210
2211 // For debugging only.
2212 private void saveToDebugUri(byte[] data) {
2213 if (mDebugUri != null) {
2214 OutputStream outputStream = null;
2215 try {
2216 outputStream = mContentResolver.openOutputStream(mDebugUri);
2217 outputStream.write(data);
2218 outputStream.close();
2219 } catch (IOException e) {
2220 Log.e(TAG, "Exception while writing debug jpeg file", e);
2221 } finally {
2222 CameraUtil.closeSilently(outputStream);
2223 }
2224 }
2225 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002226
2227 @Override
2228 public void onRemoteShutterPress() {
2229 capture();
2230 }
Michael Kolb8872c232013-01-29 10:33:22 -08002231}