blob: 010ab0629f99d0341bcaed06e33fcaa68917de3f [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
Michael Kolb8872c232013-01-29 10:33:22 -080021import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070022import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080023import android.content.Intent;
Michael Kolb8872c232013-01-29 10:33:22 -080024import android.graphics.Bitmap;
25import android.graphics.SurfaceTexture;
26import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080027import android.hardware.Camera.Parameters;
Michael Kolb8872c232013-01-29 10:33:22 -080028import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070029import android.hardware.Sensor;
30import android.hardware.SensorEvent;
31import android.hardware.SensorEventListener;
32import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.location.Location;
34import android.media.CameraProfile;
35import android.net.Uri;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070036import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080038import android.os.Handler;
39import android.os.Looper;
40import android.os.Message;
41import android.os.MessageQueue;
42import android.os.SystemClock;
43import android.provider.MediaStore;
Michael Kolb8872c232013-01-29 10:33:22 -080044import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080045import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080046import android.view.View;
Sascha Haeberling7cb8c792014-03-11 09:14:53 -070047
Sameer Padala2c8cc452013-11-05 18:49:12 -080048import com.android.camera.PhotoModule.NamedImages.NamedEntity;
49import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080050import com.android.camera.app.CameraAppUI;
Angus Kong20fad242013-11-11 18:23:46 -080051import com.android.camera.app.CameraManager.CameraAFCallback;
52import com.android.camera.app.CameraManager.CameraAFMoveCallback;
53import com.android.camera.app.CameraManager.CameraPictureCallback;
54import com.android.camera.app.CameraManager.CameraProxy;
55import com.android.camera.app.CameraManager.CameraShutterCallback;
Kevin Gabayanffbc43c2013-12-09 11:41:50 -080056import com.android.camera.app.LocationManager;
Angus Kongfd4fc0e2013-11-07 15:38:09 -080057import com.android.camera.app.MediaSaver;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -080058import com.android.camera.app.MemoryManager;
59import com.android.camera.app.MemoryManager.MemoryListener;
Angus Kong5596b4c2014-03-11 16:27:30 -070060import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070061import com.android.camera.exif.ExifInterface;
62import com.android.camera.exif.ExifTag;
63import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080064import com.android.camera.hardware.HardwareSpec;
65import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080066import com.android.camera.module.ModuleController;
Erin Dahlgren357b7672013-11-20 17:38:14 -080067import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010068import com.android.camera.settings.SettingsUtil;
Angus Kongdcccc512013-08-08 17:06:03 -070069import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070070import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070071import com.android.camera.util.GcamHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070072import com.android.camera.util.SessionStatsCollector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070073import com.android.camera.util.UsageStatistics;
74import com.android.camera2.R;
Seth Raphael5e09d012013-12-18 13:45:03 -080075import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080076
Angus Kongdcccc512013-08-08 17:06:03 -070077import java.io.File;
78import java.io.FileNotFoundException;
79import java.io.FileOutputStream;
80import java.io.IOException;
81import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010082import java.lang.ref.WeakReference;
Angus Kongdcccc512013-08-08 17:06:03 -070083import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070084import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070085
Michael Kolb8872c232013-01-29 10:33:22 -080086public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080087 extends CameraModule
88 implements PhotoController,
89 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -080090 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080091 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -080092 SensorEventListener,
93 SettingsManager.OnSettingChangedListener {
Michael Kolb8872c232013-01-29 10:33:22 -080094
Angus Kong5596b4c2014-03-11 16:27:30 -070095 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
Michael Kolb8872c232013-01-29 10:33:22 -080096
97 // We number the request code from 1000 to avoid collision with Gallery.
98 private static final int REQUEST_CROP = 1000;
99
Angus Kong13e87c42013-11-25 10:02:47 -0800100 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800101 private static final int MSG_FIRST_TIME_INIT = 1;
102 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800103
104 // The subset of parameters we need to update in setCameraParameters().
105 private static final int UPDATE_PARAM_INITIALIZE = 1;
106 private static final int UPDATE_PARAM_ZOOM = 2;
107 private static final int UPDATE_PARAM_PREFERENCE = 4;
108 private static final int UPDATE_PARAM_ALL = -1;
109
Andy Huibersdef975d2013-11-22 09:13:39 -0800110 // This is the delay before we execute onResume tasks when coming
111 // from the lock screen, to allow time for onPause to execute.
112 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800113
Ruben Brunkd7488272013-10-10 18:45:53 -0700114 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
115
Michael Kolb8872c232013-01-29 10:33:22 -0800116 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800117 private CameraProxy mCameraDevice;
118 private int mCameraId;
119 private Parameters mParameters;
120 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800121
122 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800123
Michael Kolb8872c232013-01-29 10:33:22 -0800124 // The activity is going to switch to the specified camera id. This is
125 // needed because texture copy is done in GL thread. -1 means camera is not
126 // switching.
127 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800128
129 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
130 // needed to be updated in mUpdateSet.
131 private int mUpdateSet;
132
133 private static final int SCREEN_DELAY = 2 * 60 * 1000;
134
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800135 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800136
137 private Parameters mInitialParams;
138 private boolean mFocusAreaSupported;
139 private boolean mMeteringAreaSupported;
140 private boolean mAeLockSupported;
141 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700142 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800143
144 // The degrees of the device rotated clockwise from its natural orientation.
145 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800146
147 private static final String sTempCropFilename = "crop-temp";
148
Michael Kolb8872c232013-01-29 10:33:22 -0800149 private boolean mFaceDetectionStarted = false;
150
Michael Kolb8872c232013-01-29 10:33:22 -0800151 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
152 private String mCropValue;
153 private Uri mSaveUri;
154
Ruben Brunkd217ed02013-10-08 23:31:13 -0700155 private Uri mDebugUri;
156
Angus Kongce5480e2013-01-29 17:43:48 -0800157 // We use a queue to generated names of the images to be used later
158 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800159 private NamedImages mNamedImages;
160
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800161 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800162 @Override
163 public void run() {
164 onShutterButtonClick();
165 }
166 };
167
Michael Kolb8872c232013-01-29 10:33:22 -0800168 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800169 * An unpublished intent flag requesting to return as soon as capturing is
170 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800171 */
172 private static final String EXTRA_QUICK_CAPTURE =
173 "android.intent.extra.quickCapture";
174
175 // The display rotation in degrees. This is only valid when mCameraState is
176 // not PREVIEW_STOPPED.
177 private int mDisplayRotation;
178 // The value for android.hardware.Camera.setDisplayOrientation.
179 private int mCameraDisplayOrientation;
180 // The value for UI components like indicators.
181 private int mDisplayOrientation;
182 // The value for android.hardware.Camera.Parameters.setRotation.
183 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700184 // Indicates whether we are using front camera
185 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800186 private boolean mFirstTimeInitialized;
187 private boolean mIsImageCaptureIntent;
188
Michael Kolb8872c232013-01-29 10:33:22 -0800189 private int mCameraState = PREVIEW_STOPPED;
190 private boolean mSnapshotOnIdle = false;
191
192 private ContentResolver mContentResolver;
193
194 private LocationManager mLocationManager;
Doris Liu2b906b82013-12-10 16:34:08 -0800195 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800196
Michael Kolb8872c232013-01-29 10:33:22 -0800197 private final PostViewPictureCallback mPostViewPictureCallback =
198 new PostViewPictureCallback();
199 private final RawPictureCallback mRawPictureCallback =
200 new RawPictureCallback();
201 private final AutoFocusCallback mAutoFocusCallback =
202 new AutoFocusCallback();
203 private final Object mAutoFocusMoveCallback =
204 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700205 ? new AutoFocusMoveCallback()
206 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800207
208 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
209
210 private long mFocusStartTime;
211 private long mShutterCallbackTime;
212 private long mPostViewPictureCallbackTime;
213 private long mRawPictureCallbackTime;
214 private long mJpegPictureCallbackTime;
215 private long mOnResumeTime;
216 private byte[] mJpegImageData;
217
218 // These latency time are for the CameraLatency test.
219 public long mAutoFocusTime;
220 public long mShutterLag;
221 public long mShutterToPictureDisplayedTime;
222 public long mPictureDisplayedToJpegCallbackTime;
223 public long mJpegCallbackFinishTime;
224 public long mCaptureStartTime;
225
226 // This handles everything about focus.
227 private FocusOverlayManager mFocusManager;
228
Doris Liubd1b8f92014-01-03 17:59:51 -0800229 private final int mGcamModeIndex;
Doris Liubd1b8f92014-01-03 17:59:51 -0800230
Michael Kolb8872c232013-01-29 10:33:22 -0800231 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800232
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100233 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800234
Michael Kolb8872c232013-01-29 10:33:22 -0800235 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700236 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800237 private final float[] mGData = new float[3];
238 private final float[] mMData = new float[3];
239 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700240 private int mHeading = -1;
241
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800242 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700243 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700244
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800245 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800246 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800247 @Override
248 public void onMediaSaved(Uri uri) {
249 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700250 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800251 }
252 }
253 };
Michael Kolb8872c232013-01-29 10:33:22 -0800254
Angus Kongdcccc512013-08-08 17:06:03 -0700255 private void checkDisplayRotation() {
256 // Set the display orientation if display rotation has changed.
257 // Sometimes this happens when the device is held upside
258 // down and camera app is opened. Rotation animation will
259 // take some time and the rotation value we have got may be
260 // wrong. Framework does not have a callback for this now.
261 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
262 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800263 }
Angus Kongdcccc512013-08-08 17:06:03 -0700264 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
265 mHandler.postDelayed(new Runnable() {
266 @Override
267 public void run() {
268 checkDisplayRotation();
269 }
270 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800271 }
272 }
273
274 /**
275 * This Handler is used to post message back onto the main thread of the
276 * application
277 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100278 private static class MainHandler extends Handler {
279 private final WeakReference<PhotoModule> mModule;
280
281 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800282 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100283 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800284 }
285
Michael Kolb8872c232013-01-29 10:33:22 -0800286 @Override
287 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100288 PhotoModule module = mModule.get();
289 if (module == null) {
290 return;
291 }
Michael Kolb8872c232013-01-29 10:33:22 -0800292 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800293 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100294 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800295 break;
296 }
297
Angus Kong13e87c42013-11-25 10:02:47 -0800298 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100299 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800300 break;
301 }
Michael Kolb8872c232013-01-29 10:33:22 -0800302 }
303 }
304 }
305
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700306 private void switchToGcamCapture() {
307 if (mActivity != null && mGcamModeIndex != 0) {
308 SettingsManager settingsManager = mActivity.getSettingsManager();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700309 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700310 SettingsManager.VALUE_ON);
311
312 // Disable the HDR+ button to prevent callbacks from being
313 // queued before the correct callback is attached to the button
314 // in the new module. The new module will set the enabled/disabled
315 // of this button when the module's preferred camera becomes available.
316 ButtonManager buttonManager = mActivity.getButtonManager();
317 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
318
319 // Do not post this to avoid this module switch getting interleaved with
320 // other button callbacks.
321 mActivity.onModeSelected(mGcamModeIndex);
322 }
323 }
324
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800325 /**
326 * Constructs a new photo module.
327 */
Angus Kongc4e66562013-11-22 23:03:21 -0800328 public PhotoModule(AppController app) {
329 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800330 mGcamModeIndex = app.getAndroidContext().getResources()
331 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800332 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700333
Michael Kolb8872c232013-01-29 10:33:22 -0800334 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100335 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
336 mActivity = activity;
337 // TODO: Need to look at the controller interface to see if we can get
338 // rid of passing in the activity directly.
339 mAppController = mActivity;
340
341 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
342 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800343
344 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800345 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800346
347 mContentResolver = mActivity.getContentResolver();
348
Michael Kolb8872c232013-01-29 10:33:22 -0800349 // Surface texture is from camera screen nail and startPreview needs it.
350 // This must be done before startPreview.
351 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800352
Angus Kong20fad242013-11-11 18:23:46 -0800353 mActivity.getCameraProvider().requestCamera(mCameraId);
354
Michael Kolb8872c232013-01-29 10:33:22 -0800355 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800356 mLocationManager = mActivity.getLocationManager();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800357 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800358 }
359
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800360 @Override
361 public boolean isUsingBottomBar() {
362 return true;
363 }
364
Michael Kolbd6954f32013-03-08 20:43:01 -0800365 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800366 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800367 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800368 setupCaptureParams();
369 }
370 }
371
372 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800373 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800374 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800375 startFaceDetection();
376 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800377 }
378
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +0100379 @Override
Seth Raphael30836422014-02-06 14:49:47 -0800380 public void onPreviewInitialDataReceived() {
Seth Raphael30836422014-02-06 14:49:47 -0800381 }
382
Michael Kolb8872c232013-01-29 10:33:22 -0800383 // Prompt the user to pick to record location for the very first run of
384 // camera only
385 private void locationFirstRun() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800386 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100387
Erin Dahlgren4569b702014-02-24 14:21:11 -0800388 if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION)) {
389 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800390 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800391 if (mActivity.isSecureCamera()) {
392 return;
393 }
Michael Kolb8872c232013-01-29 10:33:22 -0800394 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800395 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800396 if (backCameraId == -1) {
397 // If there is no back camera, do not show the prompt.
398 return;
399 }
Doris Liu6a83d522013-07-02 12:03:32 -0700400 mUI.showLocationDialog();
401 }
Michael Kolb8872c232013-01-29 10:33:22 -0800402
ztenghui7b265a62013-09-09 14:58:44 -0700403 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700404 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800405 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700406 }
407
408 @Override
409 public void onPreviewUIDestroyed() {
410 if (mCameraDevice == null) {
411 return;
412 }
413 mCameraDevice.setPreviewTexture(null);
414 stopPreview();
415 }
416
Doris Liu1dfe7822013-12-12 00:02:08 -0800417 @Override
418 public void startPreCaptureAnimation() {
419 mAppController.startPreCaptureAnimation();
420 }
421
Michael Kolbd6954f32013-03-08 20:43:01 -0800422 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800423 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800424 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800425 }
426
Michael Kolbd6954f32013-03-08 20:43:01 -0800427 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800428 if (mPaused) {
429 return;
430 }
Erin Dahlgren357b7672013-11-20 17:38:14 -0800431 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800432
433 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800434 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800435 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800436 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800437 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800438 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800439 if (mFocusManager != null) {
440 mFocusManager.removeMessages();
441 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800442
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700443 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700444 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700445 // Start switch camera animation. Post a message because
446 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800447 }
448
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800449 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100450 new ButtonManager.ButtonCallback() {
451 @Override
452 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800453 // At the time this callback is fired, the camera id
454 // has be set to the desired camera.
455
Angus Kong97e282a2014-03-04 18:44:49 -0800456 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100457 return;
458 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800459 // If switching to back camera, and HDR+ is still on,
460 // switch back to gcam, otherwise handle callback normally.
461 SettingsManager settingsManager = mActivity.getSettingsManager();
462 if (settingsManager.isCameraBackFacing()) {
463 if (settingsManager.requestsReturnToHdrPlus()) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700464 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800465 return;
466 }
467 }
468
Sascha Haeberlingde303232014-02-07 02:30:53 +0100469 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800470
Sascha Haeberlingde303232014-02-07 02:30:53 +0100471 Log.v(TAG, "Start to switch camera. cameraId=" + state);
472 // We need to keep a preview frame for the animation before
473 // releasing the camera. This will trigger
474 // onPreviewTextureCopied.
475 // TODO: Need to animate the camera switch
476 switchCamera();
477 }
478 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800479
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800480 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100481 new ButtonManager.ButtonCallback() {
482 @Override
483 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700484 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100485 if (GcamHelper.hasGcamCapture()) {
486 // Set the camera setting to default backfacing.
Sascha Haeberlingde303232014-02-07 02:30:53 +0100487 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700488 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100489 } else {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700490 if (settingsManager.isHdrOn()) {
491 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
492 CameraUtil.SCENE_MODE_HDR);
493 } else {
494 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
495 Parameters.SCENE_MODE_AUTO);
496 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100497 updateParametersSceneMode();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700498 mCameraDevice.setParameters(mParameters);
499 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100500 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800501 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100502 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800503
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800504 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
505 @Override
506 public void onClick(View v) {
507 onCaptureCancelled();
508 }
509 };
510
511 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
512 @Override
513 public void onClick(View v) {
514 onCaptureDone();
515 }
516 };
517
518 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
519 @Override
520 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800521 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800522 onCaptureRetake();
523 }
524 };
525
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800526 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700527 public void hardResetSettings(SettingsManager settingsManager) {
528 // PhotoModule should hard reset HDR+ to off.
529 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
530 }
531
532 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800533 public HardwareSpec getHardwareSpec() {
Erin Dahlgren49ab9222014-01-28 17:40:28 -0800534 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800535 }
536
537 @Override
538 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
539 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
540
541 bottomBarSpec.enableCamera = true;
542 bottomBarSpec.cameraCallback = mCameraCallback;
543 bottomBarSpec.enableFlash = true;
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800544 bottomBarSpec.enableHdr = true;
545 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800546 bottomBarSpec.enableGridLines = true;
Spike Spragueabf54e22014-03-27 15:41:28 -0700547 bottomBarSpec.enableExposureCompensation = true;
548 bottomBarSpec.exposureCompensationSetCallback =
549 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
550 @Override
551 public void setExposure(int value) {
552 setExposureCompensation(value);
553 }
554 };
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800555
556 if (isImageCaptureIntent()) {
557 bottomBarSpec.showCancel = true;
558 bottomBarSpec.cancelCallback = mCancelCallback;
559 bottomBarSpec.showDone = true;
560 bottomBarSpec.doneCallback = mDoneCallback;
561 bottomBarSpec.showRetake = true;
562 bottomBarSpec.retakeCallback = mRetakeCallback;
563 }
564
565 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800566 }
567
Michael Kolbd6954f32013-03-08 20:43:01 -0800568 // either open a new camera or switch cameras
569 private void openCameraCommon() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800570 mUI.onCameraOpened(mParameters);
Angus Kong0fb819b2013-10-08 13:44:19 -0700571 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800572 // Set hdr plus to default: off.
573 SettingsManager settingsManager = mActivity.getSettingsManager();
574 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700575 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800576 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800577 }
578
ztenghui7b265a62013-09-09 14:58:44 -0700579 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800580 public void updatePreviewAspectRatio(float aspectRatio) {
581 mAppController.updatePreviewAspectRatio(aspectRatio);
582 }
583
Michael Kolb8872c232013-01-29 10:33:22 -0800584 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800585 SettingsManager settingsManager = mActivity.getSettingsManager();
586 if (settingsManager == null) {
587 Log.e(TAG, "Settings manager is null!");
588 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800589 }
Spike Spragueabf54e22014-03-27 15:41:28 -0700590 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
Michael Kolb8872c232013-01-29 10:33:22 -0800591 }
592
Michael Kolb8872c232013-01-29 10:33:22 -0800593 // Snapshots can only be taken after this is called. It should be called
594 // once only. We could have done these things in onCreate() but we want to
595 // make preview screen appear as soon as possible.
596 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700597 if (mFirstTimeInitialized || mPaused) {
598 return;
599 }
Michael Kolb8872c232013-01-29 10:33:22 -0800600
Michael Kolbd6954f32013-03-08 20:43:01 -0800601 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800602
Angus Kong86d36312013-01-31 18:22:44 -0800603 // We set the listener only when both service and shutterbutton
604 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800605 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800606
Michael Kolb8872c232013-01-29 10:33:22 -0800607 mNamedImages = new NamedImages();
608
609 mFirstTimeInitialized = true;
610 addIdleHandler();
611
612 mActivity.updateStorageSpaceAndHint();
613 }
614
Michael Kolbd6954f32013-03-08 20:43:01 -0800615 // If the activity is paused and resumed, this method will be called in
616 // onResume.
617 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800618 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800619 mNamedImages = new NamedImages();
620 mUI.initializeSecondTime(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -0800621 }
622
Michael Kolb8872c232013-01-29 10:33:22 -0800623 private void addIdleHandler() {
624 MessageQueue queue = Looper.myQueue();
625 queue.addIdleHandler(new MessageQueue.IdleHandler() {
626 @Override
627 public boolean queueIdle() {
628 Storage.ensureOSXCompatible();
629 return false;
630 }
631 });
632 }
633
Michael Kolb8872c232013-01-29 10:33:22 -0800634 @Override
635 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800636 if (mFaceDetectionStarted) {
637 return;
638 }
Michael Kolb8872c232013-01-29 10:33:22 -0800639 if (mParameters.getMaxNumDetectedFaces() > 0) {
640 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700641 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700642 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800643 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700644 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800645 }
646 }
647
Michael Kolb8872c232013-01-29 10:33:22 -0800648 @Override
649 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800650 if (!mFaceDetectionStarted) {
651 return;
652 }
Michael Kolb8872c232013-01-29 10:33:22 -0800653 if (mParameters.getMaxNumDetectedFaces() > 0) {
654 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700655 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800656 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800657 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700658 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800659 }
660 }
661
Michael Kolb8872c232013-01-29 10:33:22 -0800662 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700663 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700664
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800665 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700666
Sascha Haeberling37f36112013-08-06 14:31:52 -0700667 public ShutterCallback(boolean needsAnimation) {
668 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700669 }
670
Michael Kolb8872c232013-01-29 10:33:22 -0800671 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700672 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800673 mShutterCallbackTime = System.currentTimeMillis();
674 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
675 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700676 if (mNeedsAnimation) {
677 mActivity.runOnUiThread(new Runnable() {
678 @Override
679 public void run() {
680 animateAfterShutter();
681 }
682 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700683 }
Michael Kolb8872c232013-01-29 10:33:22 -0800684 }
685 }
686
Angus Kong9ef99252013-07-18 18:04:19 -0700687 private final class PostViewPictureCallback
688 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800689 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800690 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800691 mPostViewPictureCallbackTime = System.currentTimeMillis();
692 Log.v(TAG, "mShutterToPostViewCallbackTime = "
693 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
694 + "ms");
695 }
696 }
697
Angus Kong9ef99252013-07-18 18:04:19 -0700698 private final class RawPictureCallback
699 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800700 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800701 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800702 mRawPictureCallbackTime = System.currentTimeMillis();
703 Log.v(TAG, "mShutterToRawCallbackTime = "
704 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
705 }
706 }
707
Angus Kong9ef99252013-07-18 18:04:19 -0700708 private final class JpegPictureCallback
709 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800710 Location mLocation;
711
712 public JpegPictureCallback(Location loc) {
713 mLocation = loc;
714 }
715
716 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800717 public void onPictureTaken(final byte[] jpegData, CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700718 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800719 if (mPaused) {
720 return;
721 }
Doris Liu6432cd62013-06-13 17:20:31 -0700722 if (mIsImageCaptureIntent) {
723 stopPreview();
724 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700725 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700726 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800727 }
728
729 mJpegPictureCallbackTime = System.currentTimeMillis();
730 // If postview callback has arrived, the captured image is displayed
731 // in postview callback. If not, the captured image is displayed in
732 // raw picture callback.
733 if (mPostViewPictureCallbackTime != 0) {
734 mShutterToPictureDisplayedTime =
735 mPostViewPictureCallbackTime - mShutterCallbackTime;
736 mPictureDisplayedToJpegCallbackTime =
737 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
738 } else {
739 mShutterToPictureDisplayedTime =
740 mRawPictureCallbackTime - mShutterCallbackTime;
741 mPictureDisplayedToJpegCallbackTime =
742 mJpegPictureCallbackTime - mRawPictureCallbackTime;
743 }
744 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
745 + mPictureDisplayedToJpegCallbackTime + "ms");
746
Michael Kolb8872c232013-01-29 10:33:22 -0800747 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
748 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700749 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800750 }
751
Doris Liu36e56fb2013-09-11 17:38:08 -0700752 ExifInterface exif = Exif.getExif(jpegData);
753 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -0700754 int zoomIndex = mParameters.getZoom();
755 float zoomValue = 0.01f * mParameters.getZoomRatios().get(zoomIndex);
756
Andy Huibers6b9743a2014-04-03 23:23:29 -0700757 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
Andy Huibers10c58162014-03-29 14:06:54 -0700758 UsageStatistics.instance().photoCaptureDoneEvent(
759 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
760 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers6b9743a2014-04-03 23:23:29 -0700761 isCameraFrontFacing(), hdrOn, zoomValue);
Ruben Brunkd217ed02013-10-08 23:31:13 -0700762
Ruben Brunkd7488272013-10-10 18:45:53 -0700763 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800764 // Calculate the width and the height of the jpeg.
765 Size s = mParameters.getPictureSize();
Michael Kolb8872c232013-01-29 10:33:22 -0800766 int width, height;
767 if ((mJpegRotation + orientation) % 180 == 0) {
768 width = s.width;
769 height = s.height;
770 } else {
771 width = s.height;
772 height = s.width;
773 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700774 NamedEntity name = mNamedImages.getNextNameEntity();
775 String title = (name == null) ? null : name.title;
776 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -0700777
778 // Handle debug mode outputs
779 if (mDebugUri != null) {
780 // If using a debug uri, save jpeg there.
781 saveToDebugUri(jpegData);
782
783 // Adjust the title of the debug image shown in mediastore.
784 if (title != null) {
785 title = DEBUG_IMAGE_PREFIX + title;
786 }
787 }
788
Michael Kolb8872c232013-01-29 10:33:22 -0800789 if (title == null) {
790 Log.e(TAG, "Unbalanced name/data pair");
791 } else {
Sascha Haeberling3b0ab892014-01-29 20:54:39 +0100792 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800793 date = mCaptureStartTime;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +0100794 }
Angus Kong0d00a892013-03-26 11:40:40 -0700795 if (mHeading >= 0) {
796 // heading direction has been updated by the sensor.
797 ExifTag directionRefTag = exif.buildTag(
798 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
799 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
800 ExifTag directionTag = exif.buildTag(
801 ExifInterface.TAG_GPS_IMG_DIRECTION,
802 new Rational(mHeading, 1));
803 exif.setTag(directionRefTag);
804 exif.setTag(directionTag);
805 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800806 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -0800807 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700808 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800809 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800810 // Animate capture with real jpeg data instead of a preview
811 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -0700812 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800813 } else {
814 mJpegImageData = jpegData;
815 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -0700816 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800817 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800818 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800819 }
820 }
821
822 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800823 // latency. It's true that someone else could write to the SD card
824 // in the mean time and fill it, but that could have happened
825 // between the shutter press and saving the JPEG too.
Michael Kolb8872c232013-01-29 10:33:22 -0800826 mActivity.updateStorageSpaceAndHint();
827
828 long now = System.currentTimeMillis();
829 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
Andy Huibers10c58162014-03-29 14:06:54 -0700830 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
Michael Kolb8872c232013-01-29 10:33:22 -0800831 mJpegPictureCallbackTime = 0;
832 }
833 }
834
Angus Kong9ef99252013-07-18 18:04:19 -0700835 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800836 @Override
Andy Huibers10c58162014-03-29 14:06:54 -0700837 public void onAutoFocus(boolean focused, CameraProxy camera) {
838 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800839 if (mPaused) {
840 return;
841 }
Michael Kolb8872c232013-01-29 10:33:22 -0800842
843 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -0700844 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -0800845 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -0700846 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -0800847 }
848 }
849
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700850 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -0800851 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700852 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800853 @Override
854 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700855 boolean moving, CameraProxy camera) {
856 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -0700857 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -0800858 }
859 }
860
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700861 /**
862 * This class is just a thread-safe queue for name,date holder objects.
863 */
864 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800865 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -0800866
867 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700868 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -0800869 }
870
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700871 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -0800872 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700873 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -0800874 r.date = date;
875 mQueue.add(r);
876 }
877
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700878 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800879 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700880 if (!mQueue.isEmpty()) {
881 return mQueue.remove(0);
882 }
Michael Kolb8872c232013-01-29 10:33:22 -0800883 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700884 return null;
Michael Kolb8872c232013-01-29 10:33:22 -0800885 }
886
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700887 public static class NamedEntity {
888 public String title;
889 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -0800890 }
891 }
892
893 private void setCameraState(int state) {
894 mCameraState = state;
895 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -0800896 case PREVIEW_STOPPED:
897 case SNAPSHOT_IN_PROGRESS:
898 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -0800899 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700900 break;
901 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -0800902 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700903 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800904 }
905 }
906
Sascha Haeberling37f36112013-08-06 14:31:52 -0700907 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800908 // Only animate when in full screen capture mode
909 // i.e. If monkey/a user swipes to the gallery during picture taking,
910 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700911 if (!mIsImageCaptureIntent) {
912 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700913 }
Michael Kolb8872c232013-01-29 10:33:22 -0800914 }
915
916 @Override
917 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800918 // If we are already in the middle of taking a snapshot or the image
919 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -0800920 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -0700921 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800922 return false;
923 }
924 mCaptureStartTime = System.currentTimeMillis();
925 mPostViewPictureCallbackTime = 0;
926 mJpegImageData = null;
927
Angus Kongb50b5cb2013-08-09 14:55:20 -0700928 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -0800929
930 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700931 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -0800932 }
933
934 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800935 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800936
Doris Liu3cf565c2013-02-15 10:55:37 -0800937 // We need to be consistent with the framework orientation (i.e. the
938 // orientation of the UI.) when the auto-rotate screen setting is on.
939 if (mActivity.isAutoRotateScreen()) {
940 orientation = (360 - mDisplayRotation) % 360;
941 } else {
942 orientation = mOrientation;
943 }
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +0100944 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
945 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800946 mParameters.setRotation(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -0800947 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700948 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -0800949 mCameraDevice.setParameters(mParameters);
950
Sascha Haeberling88901942013-08-28 17:49:00 -0700951 // We don't want user to press the button again while taking a
952 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -0700953 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -0700954 mCameraDevice.takePicture(mHandler,
955 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -0700956 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -0700957 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -0800958
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700959 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -0800960
961 mFaceDetectionStarted = false;
962 setCameraState(SNAPSHOT_IN_PROGRESS);
963 return true;
964 }
965
966 @Override
967 public void setFocusParameters() {
968 setCameraParameters(UPDATE_PARAM_PREFERENCE);
969 }
970
Michael Kolbd6954f32013-03-08 20:43:01 -0800971 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -0800972 // If scene mode is set, we cannot set flash mode, white balance, and
973 // focus mode, instead, we read it from driver
974 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700975 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -0800976 }
977 }
978
Doris Liu8ad8ad42014-03-27 14:43:31 -0700979 private void overrideCameraSettings(final String flashMode, final String focusMode) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800980 SettingsManager settingsManager = mActivity.getSettingsManager();
981 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
Erin Dahlgrene419b192013-12-03 13:10:27 -0800982 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -0800983 }
984
985 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800986 public void onOrientationChanged(int orientation) {
987 // We keep the last known orientation. So if the user first orient
988 // the camera then point the camera to floor or sky, we still have
989 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800990 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
991 return;
992 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700993 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800994 }
995
996 @Override
Angus Kong20fad242013-11-11 18:23:46 -0800997 public void onCameraAvailable(CameraProxy cameraProxy) {
998 if (mPaused) {
999 return;
1000 }
1001 mCameraDevice = cameraProxy;
1002
1003 initializeCapabilities();
1004
1005 // Reset zoom value index.
1006 mZoomValue = 0;
1007 if (mFocusManager == null) {
1008 initializeFocusManager();
1009 }
1010 mFocusManager.setParameters(mInitialParams);
1011
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001012 // Do camera parameter dependent initialization.
Angus Kong20fad242013-11-11 18:23:46 -08001013 mParameters = mCameraDevice.getParameters();
1014 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001015 // Set a listener which updates camera parameters based
1016 // on changed settings.
1017 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001018 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001019 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001020
Angus Kong20fad242013-11-11 18:23:46 -08001021 startPreview();
1022
1023 onCameraOpened();
1024 }
1025
1026 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001027 public void onCaptureCancelled() {
1028 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1029 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001030 }
1031
Michael Kolbd6954f32013-03-08 20:43:01 -08001032 @Override
1033 public void onCaptureRetake() {
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001034 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001035 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001036 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001037 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001038 setupPreview();
1039 }
1040
Michael Kolbd6954f32013-03-08 20:43:01 -08001041 @Override
1042 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001043 if (mPaused) {
1044 return;
1045 }
1046
1047 byte[] data = mJpegImageData;
1048
1049 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001050 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001051 // caller specifies a "save uri" then write the data to its
1052 // stream. Otherwise, pass back a scaled down version of the bitmap
1053 // directly in the extras.
1054 if (mSaveUri != null) {
1055 OutputStream outputStream = null;
1056 try {
1057 outputStream = mContentResolver.openOutputStream(mSaveUri);
1058 outputStream.write(data);
1059 outputStream.close();
1060
1061 mActivity.setResultEx(Activity.RESULT_OK);
1062 mActivity.finish();
1063 } catch (IOException ex) {
1064 // ignore exception
1065 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001066 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001067 }
1068 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001069 ExifInterface exif = Exif.getExif(data);
1070 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001071 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1072 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001073 mActivity.setResultEx(Activity.RESULT_OK,
1074 new Intent("inline-data").putExtra("data", bitmap));
1075 mActivity.finish();
1076 }
1077 } else {
1078 // Save the image to a temp file and invoke the cropper
1079 Uri tempUri = null;
1080 FileOutputStream tempStream = null;
1081 try {
1082 File path = mActivity.getFileStreamPath(sTempCropFilename);
1083 path.delete();
1084 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1085 tempStream.write(data);
1086 tempStream.close();
1087 tempUri = Uri.fromFile(path);
1088 } catch (FileNotFoundException ex) {
1089 mActivity.setResultEx(Activity.RESULT_CANCELED);
1090 mActivity.finish();
1091 return;
1092 } catch (IOException ex) {
1093 mActivity.setResultEx(Activity.RESULT_CANCELED);
1094 mActivity.finish();
1095 return;
1096 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001097 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001098 }
1099
1100 Bundle newExtras = new Bundle();
1101 if (mCropValue.equals("circle")) {
1102 newExtras.putString("circleCrop", "true");
1103 }
1104 if (mSaveUri != null) {
1105 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1106 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001107 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001108 }
1109 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001110 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001111 }
1112
Sascha Haeberling37f36112013-08-06 14:31:52 -07001113 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001114 final String CROP_ACTION = "com.android.camera.action.CROP";
1115 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001116
1117 cropIntent.setData(tempUri);
1118 cropIntent.putExtras(newExtras);
1119
1120 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1121 }
1122 }
1123
Michael Kolb8872c232013-01-29 10:33:22 -08001124 @Override
1125 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001126 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001127 }
1128
1129 @Override
1130 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001131 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001132 || (mCameraState == PREVIEW_STOPPED)) {
1133 return;
1134 }
Michael Kolb8872c232013-01-29 10:33:22 -08001135
1136 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001137 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001138 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001139 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001140 return;
1141 }
1142 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1143
Angus Kongb50b5cb2013-08-09 14:55:20 -07001144 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001145 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001146 }
Michael Kolb8872c232013-01-29 10:33:22 -08001147 // If the user wants to do a snapshot while the previous one is still
1148 // in progress, remember the fact and do it after we finish the previous
1149 // one and re-start the preview. Snapshot in progress also includes the
1150 // state that autofocus is focusing and a picture will be taken when
1151 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001152 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1153 if (!mIsImageCaptureIntent) {
1154 mSnapshotOnIdle = true;
1155 }
Michael Kolb8872c232013-01-29 10:33:22 -08001156 return;
1157 }
1158
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001159 mSnapshotOnIdle = false;
Angus Kongeaaf56d2014-02-20 11:59:10 -08001160 mFocusManager.focusAndCapture();
Michael Kolb8872c232013-01-29 10:33:22 -08001161 }
1162
Andy Huibersdef975d2013-11-22 09:13:39 -08001163 private void onResumeTasks() {
1164 Log.v(TAG, "Executing onResumeTasks.");
Angus Kong20fad242013-11-11 18:23:46 -08001165 mActivity.getCameraProvider().requestCamera(mCameraId);
1166
Michael Kolb8872c232013-01-29 10:33:22 -08001167 mJpegPictureCallbackTime = 0;
1168 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001169
1170 mOnResumeTime = SystemClock.uptimeMillis();
1171 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001172
1173 // If first time initialization is not finished, put it in the
1174 // message queue.
1175 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001176 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001177 } else {
1178 initializeSecondTime();
1179 }
Michael Kolb8872c232013-01-29 10:33:22 -08001180
Angus Kong0d00a892013-03-26 11:40:40 -07001181 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1182 if (gsensor != null) {
1183 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1184 }
1185
1186 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1187 if (msensor != null) {
1188 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1189 }
Michael Kolb8872c232013-01-29 10:33:22 -08001190 }
1191
Angus Kongc4e66562013-11-22 23:03:21 -08001192 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001193 * @return Whether the currently active camera is front-facing.
1194 */
1195 private boolean isCameraFrontFacing() {
1196 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1197 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1198 }
1199
1200 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001201 * The focus manager is the first UI related element to get initialized, and
1202 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001203 */
1204 private void initializeFocusManager() {
1205 // Create FocusManager object. startPreview needs it.
1206 // if mFocusManager not null, reuse it
1207 // otherwise create a new instance
1208 if (mFocusManager != null) {
1209 mFocusManager.removeMessages();
1210 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001211 mMirror = isCameraFrontFacing();
Angus Kongc4e66562013-11-22 23:03:21 -08001212 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1213 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001214 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1215 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001216 mInitialParams, this, mMirror,
Doris Liu482de022013-12-18 19:18:16 -08001217 mActivity.getMainLooper(), mUI.getFocusUI());
Angus Kongc4e66562013-11-22 23:03:21 -08001218 }
Doris Liu482de022013-12-18 19:18:16 -08001219 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001220 }
1221
1222 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001223 public void resume() {
1224 mPaused = false;
Doris Liu482de022013-12-18 19:18:16 -08001225 if (mFocusManager != null) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001226 // If camera is not open when resume is called, focus manager will
1227 // not
Doris Liu15b99612013-12-21 11:32:28 -08001228 // be initialized yet, in which case it will start listening to
1229 // preview area size change later in the initialization.
Doris Liu482de022013-12-18 19:18:16 -08001230 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1231 }
Doris Liu59401042014-01-14 17:51:32 -08001232
1233 if (mUI.getPreviewAreaSizeChangedListener() != null) {
1234 mAppController.addPreviewAreaSizeChangedListener(
1235 mUI.getPreviewAreaSizeChangedListener());
1236 }
1237
Angus Kongc4e66562013-11-22 23:03:21 -08001238 // Add delay on resume from lock screen only, in order to to speed up
1239 // the onResume --> onPause --> onResume cycle from lock screen.
1240 // Don't do always because letting go of thread can cause delay.
1241 String action = mActivity.getIntent().getAction();
1242 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1243 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1244 Log.v(TAG, "On resume, from lock screen.");
1245 // Note: onPauseAfterSuper() will delete this runnable, so we will
1246 // at most have 1 copy queued up.
1247 mHandler.postDelayed(new Runnable() {
Sameer Padala2c8cc452013-11-05 18:49:12 -08001248 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001249 public void run() {
1250 onResumeTasks();
1251 }
1252 }, ON_RESUME_TASKS_DELAY_MSEC);
1253 } else {
1254 Log.v(TAG, "On resume.");
1255 onResumeTasks();
1256 }
Andy Huibers10c58162014-03-29 14:06:54 -07001257 SessionStatsCollector.instance().sessionActive(true);
Angus Kongc4e66562013-11-22 23:03:21 -08001258 }
1259
1260 @Override
1261 public void pause() {
Michael Kolb8872c232013-01-29 10:33:22 -08001262 mPaused = true;
Andy Huibers10c58162014-03-29 14:06:54 -07001263 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001264
Angus Kong0d00a892013-03-26 11:40:40 -07001265 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1266 if (gsensor != null) {
1267 mSensorManager.unregisterListener(this, gsensor);
1268 }
1269
1270 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1271 if (msensor != null) {
1272 mSensorManager.unregisterListener(this, msensor);
1273 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001274
Michael Kolb8872c232013-01-29 10:33:22 -08001275 // Reset the focus first. Camera CTS does not guarantee that
1276 // cancelAutoFocus is allowed after preview stops.
1277 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1278 mCameraDevice.cancelAutoFocus();
1279 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001280
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001281 // If the camera has not been opened asynchronously yet,
1282 // and startPreview hasn't been called, then this is a no-op.
1283 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001284 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001285
Angus Kongce5480e2013-01-29 17:43:48 -08001286 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001287
Michael Kolb8872c232013-01-29 10:33:22 -08001288 // If we are in an image capture intent and has taken
1289 // a picture, we just clear it in onPause.
1290 mJpegImageData = null;
1291
Angus Kongdcccc512013-08-08 17:06:03 -07001292 // Remove the messages and runnables in the queue.
1293 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001294
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001295 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001296 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001297 mUI.onPause();
1298
Michael Kolb8872c232013-01-29 10:33:22 -08001299 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001300 if (mFocusManager != null) {
1301 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001302 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001303 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001304 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu59401042014-01-14 17:51:32 -08001305 if (mUI.getPreviewAreaSizeChangedListener() != null) {
1306 mAppController.removePreviewAreaSizeChangedListener(
1307 mUI.getPreviewAreaSizeChangedListener());
1308 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001309
1310 SettingsManager settingsManager = mActivity.getSettingsManager();
1311 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001312 }
1313
Angus Kong20fad242013-11-11 18:23:46 -08001314 @Override
1315 public void destroy() {
1316 // TODO: implement this.
1317 }
1318
1319 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001320 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001321 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001322 }
1323
1324 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001325 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001326 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001327 setDisplayOrientation();
1328 }
1329 }
1330
Michael Kolb8872c232013-01-29 10:33:22 -08001331 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001332 return isCameraIdle()
1333 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001334 }
1335
1336 @Override
1337 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001338 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001339 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001340 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001341 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001342 setCameraState(FOCUSING);
1343 }
1344
1345 @Override
1346 public void cancelAutoFocus() {
1347 mCameraDevice.cancelAutoFocus();
1348 setCameraState(IDLE);
1349 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1350 }
1351
Michael Kolb8872c232013-01-29 10:33:22 -08001352 @Override
1353 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001354 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1355 || mCameraState == SNAPSHOT_IN_PROGRESS
1356 || mCameraState == SWITCHING_CAMERA
1357 || mCameraState == PREVIEW_STOPPED) {
1358 return;
1359 }
1360
1361 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001362 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1363 return;
1364 }
Doris Liu482de022013-12-18 19:18:16 -08001365 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001366 }
1367
1368 @Override
1369 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001370 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001371 }
1372
1373 @Override
1374 public boolean onKeyDown(int keyCode, KeyEvent event) {
1375 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001376 case KeyEvent.KEYCODE_VOLUME_UP:
1377 case KeyEvent.KEYCODE_VOLUME_DOWN:
1378 case KeyEvent.KEYCODE_FOCUS:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001379 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001380 if (event.getRepeatCount() == 0) {
1381 onShutterButtonFocus(true);
1382 }
1383 return true;
1384 }
1385 return false;
1386 case KeyEvent.KEYCODE_CAMERA:
1387 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1388 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001389 }
1390 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001391 case KeyEvent.KEYCODE_DPAD_CENTER:
1392 // If we get a dpad center event without any focused view, move
1393 // the focus to the shutter button and press it.
1394 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1395 // Start auto-focus immediately to reduce shutter lag. After
1396 // the shutter button gets the focus, onShutterButtonFocus()
1397 // will be called again but it is fine.
1398 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001399 }
1400 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001401 }
1402 return false;
1403 }
1404
1405 @Override
1406 public boolean onKeyUp(int keyCode, KeyEvent event) {
1407 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001408 case KeyEvent.KEYCODE_VOLUME_UP:
1409 case KeyEvent.KEYCODE_VOLUME_DOWN:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001410 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001411 onShutterButtonClick();
1412 return true;
1413 }
1414 return false;
1415 case KeyEvent.KEYCODE_FOCUS:
1416 if (mFirstTimeInitialized) {
1417 onShutterButtonFocus(false);
1418 }
Michael Kolb8872c232013-01-29 10:33:22 -08001419 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001420 }
1421 return false;
1422 }
1423
Michael Kolb8872c232013-01-29 10:33:22 -08001424 private void closeCamera() {
1425 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001426 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001427 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001428 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong5596b4c2014-03-11 16:27:30 -07001429 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001430
Michael Kolb8872c232013-01-29 10:33:22 -08001431 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001432 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001433 mCameraDevice = null;
1434 setCameraState(PREVIEW_STOPPED);
1435 mFocusManager.onCameraReleased();
1436 }
1437 }
1438
1439 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001440 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1441 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001442 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001443 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001444 if (mFocusManager != null) {
1445 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1446 }
Doris Liu6432cd62013-06-13 17:20:31 -07001447 // Change the camera display orientation
1448 if (mCameraDevice != null) {
1449 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1450 }
Michael Kolb8872c232013-01-29 10:33:22 -08001451 }
1452
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001453 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001454 private void setupPreview() {
1455 mFocusManager.resetTouchFocus();
1456 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001457 }
1458
Angus Kong20fad242013-11-11 18:23:46 -08001459 /**
1460 * Returns whether we can/should start the preview or not.
1461 */
1462 private boolean checkPreviewPreconditions() {
1463 if (mPaused) {
1464 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001465 }
Michael Kolb8872c232013-01-29 10:33:22 -08001466
Angus Kong20fad242013-11-11 18:23:46 -08001467 if (mCameraDevice == null) {
1468 Log.w(TAG, "startPreview: camera device not ready yet.");
1469 return false;
1470 }
1471
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001472 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001473 if (st == null) {
1474 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001475 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001476 }
1477
1478 if (!mCameraPreviewParamsReady) {
1479 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001480 return false;
1481 }
1482 return true;
1483 }
1484
1485 /**
1486 * The start/stop preview should only run on the UI thread.
1487 */
1488 private void startPreview() {
1489 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001490 return;
1491 }
Angus Kong20fad242013-11-11 18:23:46 -08001492
Angus Kong5596b4c2014-03-11 16:27:30 -07001493 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001494 // ICS camera frameworks has a bug. Face detection state is not cleared
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001495 // after taking a picture. Stop the preview to work around it. The bug
1496 // was fixed in JB.
1497 if (mCameraState != PREVIEW_STOPPED) {
1498 stopPreview();
1499 }
1500
1501 setDisplayOrientation();
1502
1503 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001504 // If the focus mode is continuous autofocus, call cancelAutoFocus
1505 // to resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001506 String focusMode = mFocusManager.getFocusMode();
1507 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001508 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001509 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001510 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1511 }
1512 setCameraParameters(UPDATE_PARAM_ALL);
1513 // Let UI set its expected aspect ratio
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001514 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001515
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001516 Log.v(TAG, "startPreview");
1517 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001518
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001519 mFocusManager.onPreviewStarted();
1520 onPreviewStarted();
Andy Huibers10c58162014-03-29 14:06:54 -07001521 SessionStatsCollector.instance().previewActive(true);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001522 if (mSnapshotOnIdle) {
1523 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001524 }
1525 }
1526
Michael Kolbd6954f32013-03-08 20:43:01 -08001527 @Override
1528 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001529 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1530 Log.v(TAG, "stopPreview");
1531 mCameraDevice.stopPreview();
1532 mFaceDetectionStarted = false;
1533 }
1534 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001535 if (mFocusManager != null) {
1536 mFocusManager.onPreviewStopped();
1537 }
Andy Huibers10c58162014-03-29 14:06:54 -07001538 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001539 }
1540
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001541 @Override
Erin Dahlgren1648c362014-01-06 15:06:04 -08001542 public void onSettingChanged(SettingsManager settingsManager, int id) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001543 switch (id) {
1544 case SettingsManager.SETTING_FLASH_MODE: {
1545 updateParametersFlashMode();
1546 break;
1547 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001548 default: {
1549 // Do nothing.
1550 }
1551 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001552
1553 if (mCameraDevice != null) {
1554 mCameraDevice.setParameters(mParameters);
1555 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001556 }
1557
Michael Kolb8872c232013-01-29 10:33:22 -08001558 private void updateCameraParametersInitialize() {
1559 // Reset preview frame rate to the maximum because it may be lowered by
1560 // video camera application.
ztenghui16a35202013-09-23 11:35:36 -07001561 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters);
1562 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001563 mParameters.setPreviewFpsRange(
1564 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1565 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001566 }
1567
Angus Kongb50b5cb2013-08-09 14:55:20 -07001568 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001569
1570 // Disable video stabilization. Convenience methods not available in API
1571 // level <= 14
1572 String vstabSupported = mParameters.get("video-stabilization-supported");
1573 if ("true".equals(vstabSupported)) {
1574 mParameters.set("video-stabilization", "false");
1575 }
1576 }
1577
1578 private void updateCameraParametersZoom() {
1579 // Set zoom.
1580 if (mParameters.isZoomSupported()) {
1581 mParameters.setZoom(mZoomValue);
1582 }
1583 }
1584
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001585 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001586 private void setAutoExposureLockIfSupported() {
1587 if (mAeLockSupported) {
1588 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1589 }
1590 }
1591
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001592 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001593 private void setAutoWhiteBalanceLockIfSupported() {
1594 if (mAwbLockSupported) {
1595 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1596 }
1597 }
1598
Michael Kolb8872c232013-01-29 10:33:22 -08001599 private void setFocusAreasIfSupported() {
1600 if (mFocusAreaSupported) {
1601 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1602 }
1603 }
1604
Michael Kolb8872c232013-01-29 10:33:22 -08001605 private void setMeteringAreasIfSupported() {
1606 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001607 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1608 }
1609 }
1610
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001611 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001612 setAutoExposureLockIfSupported();
1613 setAutoWhiteBalanceLockIfSupported();
1614 setFocusAreasIfSupported();
1615 setMeteringAreasIfSupported();
1616
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001617 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07001618 mFocusManager.overrideFocusMode(null);
1619 mParameters.setFocusMode(mFocusManager.getFocusMode());
Andy Huibers10c58162014-03-29 14:06:54 -07001620 SessionStatsCollector.instance().autofocusActive(
1621 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolbd3253f22013-07-12 11:36:47 -07001622
Michael Kolb8872c232013-01-29 10:33:22 -08001623 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001624 updateParametersPictureSize();
1625
1626 // Set JPEG quality.
1627 updateParametersPictureQuality();
1628
1629 // For the following settings, we need to check if the settings are
1630 // still supported by latest driver, if not, ignore the settings.
1631
1632 // Set exposure compensation
1633 updateParametersExposureCompensation();
1634
1635 // Set the scene mode: also sets flash and white balance.
1636 updateParametersSceneMode();
1637
1638 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1639 updateAutoFocusMoveCallback();
1640 }
1641 }
1642
1643 private void updateParametersPictureSize() {
1644 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001645 String pictureSize = settingsManager
1646 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
1647 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001648
1649 List<Size> supported = mParameters.getSupportedPictureSizes();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001650 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
1651 mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001652 Size size = mParameters.getPictureSize();
1653
1654 // Set a preview size that is closest to the viewfinder height and has
1655 // the right aspect ratio.
1656 List<Size> sizes = mParameters.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001657 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Michael Kolb8872c232013-01-29 10:33:22 -08001658 (double) size.width / size.height);
1659 Size original = mParameters.getPreviewSize();
1660 if (!original.equals(optimalSize)) {
1661 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
Doris Liu6432cd62013-06-13 17:20:31 -07001662
Michael Kolb8872c232013-01-29 10:33:22 -08001663 // Zoom related settings will be changed for different preview
1664 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001665 if (mHandler.getLooper() == Looper.myLooper()) {
1666 // On UI thread only, not when camera starts up
1667 setupPreview();
1668 } else {
1669 mCameraDevice.setParameters(mParameters);
1670 }
Michael Kolb8872c232013-01-29 10:33:22 -08001671 mParameters = mCameraDevice.getParameters();
1672 }
Doris Liu95405742013-11-05 15:25:26 -08001673
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001674 if (optimalSize.width != 0 && optimalSize.height != 0) {
Doris Liu95405742013-11-05 15:25:26 -08001675 mUI.updatePreviewAspectRatio((float) optimalSize.width
1676 / (float) optimalSize.height);
1677 }
Michael Kolb8872c232013-01-29 10:33:22 -08001678 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001679 }
Michael Kolb8872c232013-01-29 10:33:22 -08001680
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001681 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08001682 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1683 CameraProfile.QUALITY_HIGH);
1684 mParameters.setJpegQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001685 }
Michael Kolb8872c232013-01-29 10:33:22 -08001686
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001687 private void updateParametersExposureCompensation() {
1688 SettingsManager settingsManager = mActivity.getSettingsManager();
Spike Spragueabf54e22014-03-27 15:41:28 -07001689 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
1690 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
1691 int max = mParameters.getMaxExposureCompensation();
1692 int min = mParameters.getMinExposureCompensation();
1693 if (value >= min && value <= max) {
1694 mParameters.setExposureCompensation(value);
1695 } else {
1696 Log.w(TAG, "invalid exposure range: " + value);
1697 }
Michael Kolb8872c232013-01-29 10:33:22 -08001698 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07001699 // If exposure compensation is not enabled, reset the exposure compensation value.
1700 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08001701 }
Spike Spragueabf54e22014-03-27 15:41:28 -07001702
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001703 }
1704
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001705 private void updateParametersSceneMode() {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001706 SettingsManager settingsManager = mActivity.getSettingsManager();
1707
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001708 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001709 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
1710 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1711 mParameters.setSceneMode(mSceneMode);
1712
1713 // Setting scene mode will change the settings of flash mode,
1714 // white balance, and focus mode. Here we read back the
1715 // parameters, so we can know those settings.
1716 mCameraDevice.setParameters(mParameters);
1717 mParameters = mCameraDevice.getParameters();
1718 }
1719 } else {
1720 mSceneMode = mParameters.getSceneMode();
1721 if (mSceneMode == null) {
1722 mSceneMode = Parameters.SCENE_MODE_AUTO;
1723 }
1724 }
1725
Michael Kolb8872c232013-01-29 10:33:22 -08001726 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1727 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001728 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001729
Michael Kolb8872c232013-01-29 10:33:22 -08001730 // Set focus mode.
1731 mFocusManager.overrideFocusMode(null);
1732 mParameters.setFocusMode(mFocusManager.getFocusMode());
1733 } else {
1734 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1735 }
Michael Kolb8872c232013-01-29 10:33:22 -08001736 }
1737
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001738 private void updateParametersFlashMode() {
1739 SettingsManager settingsManager = mActivity.getSettingsManager();
1740
1741 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
1742 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1743 if (CameraUtil.isSupported(flashMode, supportedFlash)) {
1744 mParameters.setFlashMode(flashMode);
1745 }
1746 }
1747
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001748 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001749 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001750 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07001751 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07001752 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001753 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07001754 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001755 }
1756 }
1757
Spike Spragueabf54e22014-03-27 15:41:28 -07001758 /**
1759 * Sets the exposure compensation to the given value and also updates settings.
1760 *
1761 * @param value exposure compensation value to be set
1762 */
1763 public void setExposureCompensation(int value) {
1764 int max = mParameters.getMaxExposureCompensation();
1765 int min = mParameters.getMinExposureCompensation();
1766 if (value >= min && value <= max) {
1767 mParameters.setExposureCompensation(value);
1768 SettingsManager settingsManager = mActivity.getSettingsManager();
1769 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
1770 } else {
1771 Log.w(TAG, "invalid exposure range: " + value);
1772 }
1773 }
1774
Michael Kolb8872c232013-01-29 10:33:22 -08001775 // We separate the parameters into several subsets, so we can update only
1776 // the subsets actually need updating. The PREFERENCE set needs extra
1777 // locking because the preference can be changed from GLThread as well.
1778 private void setCameraParameters(int updateSet) {
1779 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1780 updateCameraParametersInitialize();
1781 }
1782
1783 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1784 updateCameraParametersZoom();
1785 }
1786
1787 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001788 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08001789 }
1790
1791 mCameraDevice.setParameters(mParameters);
1792 }
1793
1794 // If the Camera is idle, update the parameters immediately, otherwise
1795 // accumulate them in mUpdateSet and update later.
1796 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1797 mUpdateSet |= additionalUpdateSet;
1798 if (mCameraDevice == null) {
1799 // We will update all the parameters when we open the device, so
1800 // we don't need to do anything now.
1801 mUpdateSet = 0;
1802 return;
1803 } else if (isCameraIdle()) {
1804 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001805 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001806 mUpdateSet = 0;
1807 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08001808 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1809 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08001810 }
1811 }
1812 }
1813
ztenghui7b265a62013-09-09 14:58:44 -07001814 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001815 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001816 return (mCameraState == IDLE) ||
1817 (mCameraState == PREVIEW_STOPPED) ||
1818 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001819 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08001820 }
1821
ztenghui7b265a62013-09-09 14:58:44 -07001822 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001823 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001824 String action = mActivity.getIntent().getAction();
1825 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001826 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08001827 }
1828
1829 private void setupCaptureParams() {
1830 Bundle myExtras = mActivity.getIntent().getExtras();
1831 if (myExtras != null) {
1832 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1833 mCropValue = myExtras.getString("crop");
1834 }
1835 }
1836
Michael Kolb8872c232013-01-29 10:33:22 -08001837 private void initializeCapabilities() {
1838 mInitialParams = mCameraDevice.getParameters();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001839 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
1840 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
1841 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams);
1842 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams);
Angus Kongdcccc512013-08-08 17:06:03 -07001843 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07001844 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08001845 }
1846
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001847 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08001848 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001849 public int onZoomChanged(int index) {
1850 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001851 if (mPaused) {
1852 return index;
1853 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001854 mZoomValue = index;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001855 if (mParameters == null || mCameraDevice == null) {
1856 return index;
1857 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001858 // Set zoom parameters asynchronously
1859 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07001860 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08001861 Parameters p = mCameraDevice.getParameters();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001862 if (p != null) {
1863 return p.getZoom();
1864 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001865 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001866 }
1867
1868 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001869 public int getCameraState() {
1870 return mCameraState;
1871 }
1872
1873 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001874 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07001875 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08001876 }
Angus Kong86d36312013-01-31 18:22:44 -08001877
1878 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001879 public void onLowMemory() {
1880 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08001881 }
Angus Kong0d00a892013-03-26 11:40:40 -07001882
1883 @Override
1884 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1885 }
1886
1887 @Override
1888 public void onSensorChanged(SensorEvent event) {
1889 int type = event.sensor.getType();
1890 float[] data;
1891 if (type == Sensor.TYPE_ACCELEROMETER) {
1892 data = mGData;
1893 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1894 data = mMData;
1895 } else {
1896 // we should not be here.
1897 return;
1898 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001899 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07001900 data[i] = event.values[i];
1901 }
1902 float[] orientation = new float[3];
1903 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
1904 SensorManager.getOrientation(mR, orientation);
1905 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
1906 if (mHeading < 0) {
1907 mHeading += 360;
1908 }
Angus Kong0d00a892013-03-26 11:40:40 -07001909 }
Doris Liu6432cd62013-06-13 17:20:31 -07001910
Ruben Brunkd217ed02013-10-08 23:31:13 -07001911 // For debugging only.
1912 public void setDebugUri(Uri uri) {
1913 mDebugUri = uri;
1914 }
1915
1916 // For debugging only.
1917 private void saveToDebugUri(byte[] data) {
1918 if (mDebugUri != null) {
1919 OutputStream outputStream = null;
1920 try {
1921 outputStream = mContentResolver.openOutputStream(mDebugUri);
1922 outputStream.write(data);
1923 outputStream.close();
1924 } catch (IOException e) {
1925 Log.e(TAG, "Exception while writing debug jpeg file", e);
1926 } finally {
1927 CameraUtil.closeSilently(outputStream);
1928 }
1929 }
1930 }
Michael Kolb8872c232013-01-29 10:33:22 -08001931}