blob: 8a6d66d211f8bbb387fcff9fbd4adcceed435fb7 [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.content.res.Configuration;
25import android.graphics.Bitmap;
Doris Liu36ebcb12013-10-28 14:44:24 -070026import android.graphics.Rect;
Michael Kolb8872c232013-01-29 10:33:22 -080027import android.graphics.SurfaceTexture;
28import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080029import android.hardware.Camera.Parameters;
Michael Kolb8872c232013-01-29 10:33:22 -080030import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070031import android.hardware.Sensor;
32import android.hardware.SensorEvent;
33import android.hardware.SensorEventListener;
34import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080035import android.location.Location;
36import android.media.CameraProfile;
37import android.net.Uri;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070038import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080039import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080040import android.os.Handler;
41import android.os.Looper;
42import android.os.Message;
43import android.os.MessageQueue;
44import android.os.SystemClock;
45import android.provider.MediaStore;
46import android.util.Log;
Michael Kolb8872c232013-01-29 10:33:22 -080047import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080048import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080049import android.view.View;
Doris Liu773e1c92013-12-02 17:35:03 -080050import android.view.ViewGroup;
Michael Kolb8872c232013-01-29 10:33:22 -080051
Sameer Padala2c8cc452013-11-05 18:49:12 -080052import com.android.camera.PhotoModule.NamedImages.NamedEntity;
53import com.android.camera.app.AppController;
Angus Kong20fad242013-11-11 18:23:46 -080054import com.android.camera.app.CameraManager.CameraAFCallback;
55import com.android.camera.app.CameraManager.CameraAFMoveCallback;
56import com.android.camera.app.CameraManager.CameraPictureCallback;
57import com.android.camera.app.CameraManager.CameraProxy;
58import com.android.camera.app.CameraManager.CameraShutterCallback;
Marco Nelissen0744e4a2013-11-22 01:47:37 +000059import com.android.camera.app.AppController;
Angus Kongfd4fc0e2013-11-07 15:38:09 -080060import com.android.camera.app.MediaSaver;
ztenghuia16e7b52013-08-23 11:47:56 -070061import com.android.camera.exif.ExifInterface;
62import com.android.camera.exif.ExifTag;
63import com.android.camera.exif.Rational;
Angus Kong20fad242013-11-11 18:23:46 -080064import com.android.camera.module.ModuleController;
Erin Dahlgren357b7672013-11-20 17:38:14 -080065import com.android.camera.settings.SettingsManager;
Michael Kolbd6954f32013-03-08 20:43:01 -080066import com.android.camera.ui.CountDownView.OnCountDownFinishedListener;
Doris Liu1c94b7d2013-11-09 19:13:44 -080067import com.android.camera.ui.ModeListView;
Michael Kolb8872c232013-01-29 10:33:22 -080068import com.android.camera.ui.RotateTextToast;
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;
Sameer Padala2c8cc452013-11-05 18:49:12 -080072import com.android.camera.util.SmartCameraHelper;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070073import com.android.camera.util.UsageStatistics;
74import com.android.camera2.R;
Michael Kolb8872c232013-01-29 10:33:22 -080075
Angus Kongdcccc512013-08-08 17:06:03 -070076import java.io.File;
77import java.io.FileNotFoundException;
78import java.io.FileOutputStream;
79import java.io.IOException;
80import java.io.OutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070081import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070082import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070083
Michael Kolb8872c232013-01-29 10:33:22 -080084public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080085 extends CameraModule
86 implements PhotoController,
87 ModuleController,
88 FocusOverlayManager.Listener,
89 CameraPreference.OnPreferenceChangedListener,
90 ShutterButton.OnShutterButtonListener, MediaSaver.QueueListener,
91 OnCountDownFinishedListener,
92 SensorEventListener {
Michael Kolb8872c232013-01-29 10:33:22 -080093
94 private static final String TAG = "CAM_PhotoModule";
95
96 // We number the request code from 1000 to avoid collision with Gallery.
97 private static final int REQUEST_CROP = 1000;
98
Angus Kong13e87c42013-11-25 10:02:47 -080099 // Messages defined for the UI thread handler.
100 private static final int MSG_SETUP_PREVIEW = 1;
101 private static final int MSG_FIRST_TIME_INIT = 2;
102 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 3;
103 private static final int MSG_SHOW_TAP_TO_FOCUS_TOAST = 4;
104 private static final int MSG_SWITCH_CAMERA = 5;
105 private static final int MSG_SWITCH_CAMERA_START_ANIMATION = 6;
106 private static final int MSG_CAMERA_OPEN_DONE = 7;
107 private static final int MSG_OPEN_CAMERA_FAIL = 8;
108 private static final int MSG_CAMERA_DISABLED = 9;
109 private static final int MSG_SWITCH_TO_GCAM_MODULE = 10;
Michael Kolb8872c232013-01-29 10:33:22 -0800110
111 // The subset of parameters we need to update in setCameraParameters().
112 private static final int UPDATE_PARAM_INITIALIZE = 1;
113 private static final int UPDATE_PARAM_ZOOM = 2;
114 private static final int UPDATE_PARAM_PREFERENCE = 4;
115 private static final int UPDATE_PARAM_ALL = -1;
116
Andy Huibersdef975d2013-11-22 09:13:39 -0800117 // This is the delay before we execute onResume tasks when coming
118 // from the lock screen, to allow time for onPause to execute.
119 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800120
Ruben Brunkd7488272013-10-10 18:45:53 -0700121 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
122
Michael Kolb8872c232013-01-29 10:33:22 -0800123 // copied from Camera hierarchy
124 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800125 private CameraProxy mCameraDevice;
126 private int mCameraId;
127 private Parameters mParameters;
128 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800129
130 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800131
Michael Kolb8872c232013-01-29 10:33:22 -0800132 // The activity is going to switch to the specified camera id. This is
133 // needed because texture copy is done in GL thread. -1 means camera is not
134 // switching.
135 protected int mPendingSwitchCameraId = -1;
136 private boolean mOpenCameraFail;
137 private boolean mCameraDisabled;
138
139 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
140 // needed to be updated in mUpdateSet.
141 private int mUpdateSet;
142
143 private static final int SCREEN_DELAY = 2 * 60 * 1000;
144
145 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800146
147 private Parameters mInitialParams;
148 private boolean mFocusAreaSupported;
149 private boolean mMeteringAreaSupported;
150 private boolean mAeLockSupported;
151 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700152 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800153
154 // The degrees of the device rotated clockwise from its natural orientation.
155 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800156
157 private static final String sTempCropFilename = "crop-temp";
158
Michael Kolb8872c232013-01-29 10:33:22 -0800159 private boolean mFaceDetectionStarted = false;
160
Michael Kolb8872c232013-01-29 10:33:22 -0800161 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
162 private String mCropValue;
163 private Uri mSaveUri;
164
Ruben Brunkd217ed02013-10-08 23:31:13 -0700165 private Uri mDebugUri;
166
Angus Kongce5480e2013-01-29 17:43:48 -0800167 // We use a queue to generated names of the images to be used later
168 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800169 private NamedImages mNamedImages;
170
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800171 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800172 @Override
173 public void run() {
174 onShutterButtonClick();
175 }
176 };
177
Michael Kolb8872c232013-01-29 10:33:22 -0800178 /**
179 * An unpublished intent flag requesting to return as soon as capturing
180 * is completed.
181 *
182 * TODO: consider publishing by moving into MediaStore.
183 */
184 private static final String EXTRA_QUICK_CAPTURE =
185 "android.intent.extra.quickCapture";
186
187 // The display rotation in degrees. This is only valid when mCameraState is
188 // not PREVIEW_STOPPED.
189 private int mDisplayRotation;
190 // The value for android.hardware.Camera.setDisplayOrientation.
191 private int mCameraDisplayOrientation;
192 // The value for UI components like indicators.
193 private int mDisplayOrientation;
194 // The value for android.hardware.Camera.Parameters.setRotation.
195 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700196 // Indicates whether we are using front camera
197 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800198 private boolean mFirstTimeInitialized;
199 private boolean mIsImageCaptureIntent;
200
Michael Kolb8872c232013-01-29 10:33:22 -0800201 private int mCameraState = PREVIEW_STOPPED;
202 private boolean mSnapshotOnIdle = false;
203
204 private ContentResolver mContentResolver;
205
206 private LocationManager mLocationManager;
207
Michael Kolb8872c232013-01-29 10:33:22 -0800208 private final PostViewPictureCallback mPostViewPictureCallback =
209 new PostViewPictureCallback();
210 private final RawPictureCallback mRawPictureCallback =
211 new RawPictureCallback();
212 private final AutoFocusCallback mAutoFocusCallback =
213 new AutoFocusCallback();
214 private final Object mAutoFocusMoveCallback =
215 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700216 ? new AutoFocusMoveCallback()
217 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800218
219 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
220
221 private long mFocusStartTime;
222 private long mShutterCallbackTime;
223 private long mPostViewPictureCallbackTime;
224 private long mRawPictureCallbackTime;
225 private long mJpegPictureCallbackTime;
226 private long mOnResumeTime;
227 private byte[] mJpegImageData;
228
229 // These latency time are for the CameraLatency test.
230 public long mAutoFocusTime;
231 public long mShutterLag;
232 public long mShutterToPictureDisplayedTime;
233 public long mPictureDisplayedToJpegCallbackTime;
234 public long mJpegCallbackFinishTime;
235 public long mCaptureStartTime;
236
237 // This handles everything about focus.
238 private FocusOverlayManager mFocusManager;
239
Michael Kolb8872c232013-01-29 10:33:22 -0800240 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800241
242 private final Handler mHandler = new MainHandler();
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800243
Michael Kolb8872c232013-01-29 10:33:22 -0800244 private PreferenceGroup mPreferenceGroup;
245
246 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700247 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800248 private final float[] mGData = new float[3];
249 private final float[] mMData = new float[3];
250 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700251 private int mHeading = -1;
252
Angus Kongdcccc512013-08-08 17:06:03 -0700253 // True if all the parameters needed to start preview is ready.
254 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700255
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800256 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800257 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800258 @Override
259 public void onMediaSaved(Uri uri) {
260 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700261 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800262 }
263 }
264 };
Michael Kolb8872c232013-01-29 10:33:22 -0800265
Angus Kongdcccc512013-08-08 17:06:03 -0700266 private void checkDisplayRotation() {
267 // Set the display orientation if display rotation has changed.
268 // Sometimes this happens when the device is held upside
269 // down and camera app is opened. Rotation animation will
270 // take some time and the rotation value we have got may be
271 // wrong. Framework does not have a callback for this now.
272 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
273 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800274 }
Angus Kongdcccc512013-08-08 17:06:03 -0700275 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
276 mHandler.postDelayed(new Runnable() {
277 @Override
278 public void run() {
279 checkDisplayRotation();
280 }
281 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800282 }
283 }
284
285 /**
286 * This Handler is used to post message back onto the main thread of the
287 * application
288 */
289 private class MainHandler extends Handler {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800290 public MainHandler() {
291 super(Looper.getMainLooper());
292 }
293
Michael Kolb8872c232013-01-29 10:33:22 -0800294 @Override
295 public void handleMessage(Message msg) {
296 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800297 case MSG_SETUP_PREVIEW: {
Michael Kolb8872c232013-01-29 10:33:22 -0800298 setupPreview();
299 break;
300 }
301
Angus Kong13e87c42013-11-25 10:02:47 -0800302 case MSG_FIRST_TIME_INIT: {
Michael Kolb8872c232013-01-29 10:33:22 -0800303 initializeFirstTime();
304 break;
305 }
306
Angus Kong13e87c42013-11-25 10:02:47 -0800307 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Michael Kolb8872c232013-01-29 10:33:22 -0800308 setCameraParametersWhenIdle(0);
309 break;
310 }
311
Angus Kong13e87c42013-11-25 10:02:47 -0800312 case MSG_SHOW_TAP_TO_FOCUS_TOAST: {
Michael Kolb8872c232013-01-29 10:33:22 -0800313 showTapToFocusToast();
314 break;
315 }
316
Angus Kong13e87c42013-11-25 10:02:47 -0800317 case MSG_SWITCH_CAMERA: {
Michael Kolb8872c232013-01-29 10:33:22 -0800318 switchCamera();
319 break;
320 }
321
Angus Kong13e87c42013-11-25 10:02:47 -0800322 case MSG_SWITCH_CAMERA_START_ANIMATION: {
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700323 // TODO: Need to revisit
324 // ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -0800325 break;
326 }
327
Angus Kong13e87c42013-11-25 10:02:47 -0800328 case MSG_CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800329 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800330 break;
331 }
332
Angus Kong13e87c42013-11-25 10:02:47 -0800333 case MSG_OPEN_CAMERA_FAIL: {
Michael Kolb8872c232013-01-29 10:33:22 -0800334 mOpenCameraFail = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700335 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800336 R.string.cannot_connect_camera);
337 break;
338 }
339
Angus Kong13e87c42013-11-25 10:02:47 -0800340 case MSG_CAMERA_DISABLED: {
Michael Kolb8872c232013-01-29 10:33:22 -0800341 mCameraDisabled = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700342 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800343 R.string.camera_disabled);
344 break;
345 }
ztenghui367c7c82013-10-16 14:43:26 -0700346
Angus Kong13e87c42013-11-25 10:02:47 -0800347 case MSG_SWITCH_TO_GCAM_MODULE: {
Erin Dahlgrend7b8cb52013-11-14 17:25:37 -0800348 mActivity.onModeSelected(ModeListView.MODE_GCAM);
ztenghui367c7c82013-10-16 14:43:26 -0700349 }
Michael Kolb8872c232013-01-29 10:33:22 -0800350 }
351 }
352 }
353
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800354 /**
355 * Constructs a new photo module.
356 */
Angus Kongc4e66562013-11-22 23:03:21 -0800357 public PhotoModule(AppController app) {
358 super(app);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800359 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700360
Angus Kong13e87c42013-11-25 10:02:47 -0800361
Michael Kolb8872c232013-01-29 10:33:22 -0800362 @Override
Angus Kong13e87c42013-11-25 10:02:47 -0800363 public void init(AppController app, boolean isSecureCamera, boolean isCaptureIntent) {
364 mActivity = (CameraActivity) app.getAndroidContext();
365 mUI = new PhotoUI(mActivity, this, app.getModuleLayoutRoot());
Erin Dahlgren357b7672013-11-20 17:38:14 -0800366
367 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800368 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800369
370 mContentResolver = mActivity.getContentResolver();
371
Michael Kolb8872c232013-01-29 10:33:22 -0800372 // Surface texture is from camera screen nail and startPreview needs it.
373 // This must be done before startPreview.
374 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800375
Angus Kong20fad242013-11-11 18:23:46 -0800376 mActivity.getCameraProvider().requestCamera(mCameraId);
377
Michael Kolb8872c232013-01-29 10:33:22 -0800378 initializeControlByIntent();
379 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800380 mLocationManager = mActivity.getLocationManager();
Angus Kong0d00a892013-03-26 11:40:40 -0700381 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800382 }
383
384 private void initializeControlByIntent() {
385 mUI.initializeControlByIntent();
386 if (mIsImageCaptureIntent) {
387 setupCaptureParams();
388 }
389 }
390
391 private void onPreviewStarted() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800392 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800393 startFaceDetection();
Sameer Padala2c8cc452013-11-05 18:49:12 -0800394 startSmartCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800395 locationFirstRun();
Michael Kolb8872c232013-01-29 10:33:22 -0800396 }
397
398 // Prompt the user to pick to record location for the very first run of
399 // camera only
400 private void locationFirstRun() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800401 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800402 if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION)) {
Michael Kolb8872c232013-01-29 10:33:22 -0800403 return;
404 }
405 if (mActivity.isSecureCamera()) return;
406 // Check if the back camera exists
407 int backCameraId = CameraHolder.instance().getBackCameraId();
408 if (backCameraId == -1) {
409 // If there is no back camera, do not show the prompt.
410 return;
411 }
Doris Liu6a83d522013-07-02 12:03:32 -0700412 mUI.showLocationDialog();
413 }
Michael Kolb8872c232013-01-29 10:33:22 -0800414
ztenghui7b265a62013-09-09 14:58:44 -0700415 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700416 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800417 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700418 }
419
420 @Override
421 public void onPreviewUIDestroyed() {
422 if (mCameraDevice == null) {
423 return;
424 }
425 mCameraDevice.setPreviewTexture(null);
426 stopPreview();
427 }
428
Michael Kolbd6954f32013-03-08 20:43:01 -0800429 private void onCameraOpened() {
430 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800431 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800432
433 int width = root.getWidth();
434 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800435 mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800436 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800437 }
438
Michael Kolbd6954f32013-03-08 20:43:01 -0800439 private void switchCamera() {
440 if (mPaused) return;
Erin Dahlgren357b7672013-11-20 17:38:14 -0800441 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800442
443 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
444 mCameraId = mPendingSwitchCameraId;
445 mPendingSwitchCameraId = -1;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800446 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800447 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800448
Michael Kolbd6954f32013-03-08 20:43:01 -0800449 mUI.collapseCameraControls();
450 mUI.clearFaces();
451 if (mFocusManager != null) mFocusManager.removeMessages();
452
Erin Dahlgren357b7672013-11-20 17:38:14 -0800453 // TODO: this needs to be brought into onCameraAvailable();
Angus Kong20fad242013-11-11 18:23:46 -0800454 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
Doris Liu29da2db2013-08-28 14:28:45 -0700455 mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
456 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700457 // Start switch camera animation. Post a message because
458 // onFrameAvailable from the old camera may already exist.
Angus Kong13e87c42013-11-25 10:02:47 -0800459 mHandler.sendEmptyMessage(MSG_SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800460 }
461
Michael Kolbd6954f32013-03-08 20:43:01 -0800462 // either open a new camera or switch cameras
463 private void openCameraCommon() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800464 mPreferenceGroup = loadPreferenceGroup();
Michael Kolbd6954f32013-03-08 20:43:01 -0800465
Erin Dahlgren357b7672013-11-20 17:38:14 -0800466 // TODO: remove dependency on PreferenceGroup
467 mUI.onCameraOpened(mPreferenceGroup, mParameters, this);
Angus Kong0fb819b2013-10-08 13:44:19 -0700468 if (mIsImageCaptureIntent) {
469 mUI.overrideSettings(CameraSettings.KEY_CAMERA_HDR_PLUS,
470 mActivity.getString(R.string.setting_off_value));
471 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800472 updateSceneMode();
473 showTapToFocusToastIfNeeded();
Michael Kolb8872c232013-01-29 10:33:22 -0800474 }
475
ztenghui7b265a62013-09-09 14:58:44 -0700476 @Override
Doris Liu36ebcb12013-10-28 14:44:24 -0700477 public void onPreviewRectChanged(Rect previewRect) {
478 if (mFocusManager != null) mFocusManager.setPreviewRect(previewRect);
Michael Kolbd6954f32013-03-08 20:43:01 -0800479 }
Michael Kolb8872c232013-01-29 10:33:22 -0800480
481 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800482 SettingsManager settingsManager = mActivity.getSettingsManager();
483 if (settingsManager == null) {
484 Log.e(TAG, "Settings manager is null!");
485 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800486 }
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800487 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE);
Michael Kolb8872c232013-01-29 10:33:22 -0800488 }
489
Michael Kolb8872c232013-01-29 10:33:22 -0800490 // Snapshots can only be taken after this is called. It should be called
491 // once only. We could have done these things in onCreate() but we want to
492 // make preview screen appear as soon as possible.
493 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700494 if (mFirstTimeInitialized || mPaused) {
495 return;
496 }
Michael Kolb8872c232013-01-29 10:33:22 -0800497
498 // Initialize location service.
Erin Dahlgren357b7672013-11-20 17:38:14 -0800499 SettingsController settingsController = mActivity.getSettingsController();
500 settingsController.syncLocationManager();
Michael Kolb8872c232013-01-29 10:33:22 -0800501
Michael Kolbd6954f32013-03-08 20:43:01 -0800502 mUI.initializeFirstTime();
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800503 MediaSaver s = getServices().getMediaSaver();
Angus Kong86d36312013-01-31 18:22:44 -0800504 // We set the listener only when both service and shutterbutton
505 // are initialized.
506 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800507 s.setQueueListener(this);
Angus Kong86d36312013-01-31 18:22:44 -0800508 }
Michael Kolb8872c232013-01-29 10:33:22 -0800509
Michael Kolb8872c232013-01-29 10:33:22 -0800510 mNamedImages = new NamedImages();
511
512 mFirstTimeInitialized = true;
513 addIdleHandler();
514
515 mActivity.updateStorageSpaceAndHint();
516 }
517
Michael Kolbd6954f32013-03-08 20:43:01 -0800518 // If the activity is paused and resumed, this method will be called in
519 // onResume.
520 private void initializeSecondTime() {
521 // Start location update if needed.
Erin Dahlgren357b7672013-11-20 17:38:14 -0800522 SettingsController settingsController = mActivity.getSettingsController();
523 settingsController.syncLocationManager();
524
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800525 MediaSaver s = getServices().getMediaSaver();
Michael Kolbd6954f32013-03-08 20:43:01 -0800526 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800527 s.setQueueListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800528 }
529 mNamedImages = new NamedImages();
530 mUI.initializeSecondTime(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -0800531 }
532
Michael Kolb8872c232013-01-29 10:33:22 -0800533 private void showTapToFocusToastIfNeeded() {
534 // Show the tap to focus toast if this is the first start.
Erin Dahlgren357b7672013-11-20 17:38:14 -0800535 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800536 boolean showHint = settingsManager.getBoolean(
537 SettingsManager.SETTING_CAMERA_FIRST_USE_HINT_SHOWN);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800538 // CONVERT THIS SETTING TO A STRING
539 if (mFocusAreaSupported && showHint) {
Michael Kolb8872c232013-01-29 10:33:22 -0800540 // Delay the toast for one second to wait for orientation.
Angus Kong13e87c42013-11-25 10:02:47 -0800541 mHandler.sendEmptyMessageDelayed(MSG_SHOW_TAP_TO_FOCUS_TOAST, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -0800542 }
543 }
544
545 private void addIdleHandler() {
546 MessageQueue queue = Looper.myQueue();
547 queue.addIdleHandler(new MessageQueue.IdleHandler() {
548 @Override
549 public boolean queueIdle() {
550 Storage.ensureOSXCompatible();
551 return false;
552 }
553 });
554 }
555
Sameer Padala2c8cc452013-11-05 18:49:12 -0800556 private void startSmartCamera() {
557 SmartCameraHelper.register(mCameraDevice, mParameters.getPreviewSize(), mActivity,
Doris Liu773e1c92013-12-02 17:35:03 -0800558 (ViewGroup) mActivity.findViewById(R.id.camera_app_root));
Sameer Padala2c8cc452013-11-05 18:49:12 -0800559 }
560
561 private void stopSmartCamera() {
562 SmartCameraHelper.tearDown();
563 }
564
Michael Kolb8872c232013-01-29 10:33:22 -0800565 @Override
566 public void startFaceDetection() {
Michael Kolb8872c232013-01-29 10:33:22 -0800567 if (mFaceDetectionStarted) return;
568 if (mParameters.getMaxNumDetectedFaces() > 0) {
569 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800570 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800571 mUI.onStartFaceDetection(mDisplayOrientation,
572 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
Angus Kong9e765522013-07-31 14:05:20 -0700573 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800574 mCameraDevice.startFaceDetection();
575 }
576 }
577
Michael Kolb8872c232013-01-29 10:33:22 -0800578 @Override
579 public void stopFaceDetection() {
Michael Kolb8872c232013-01-29 10:33:22 -0800580 if (!mFaceDetectionStarted) return;
581 if (mParameters.getMaxNumDetectedFaces() > 0) {
582 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700583 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800584 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800585 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800586 }
587 }
588
Michael Kolb8872c232013-01-29 10:33:22 -0800589 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700590 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700591
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800592 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700593
Sascha Haeberling37f36112013-08-06 14:31:52 -0700594 public ShutterCallback(boolean needsAnimation) {
595 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700596 }
597
Michael Kolb8872c232013-01-29 10:33:22 -0800598 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700599 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800600 mShutterCallbackTime = System.currentTimeMillis();
601 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
602 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700603 if (mNeedsAnimation) {
604 mActivity.runOnUiThread(new Runnable() {
605 @Override
606 public void run() {
607 animateAfterShutter();
608 }
609 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700610 }
Michael Kolb8872c232013-01-29 10:33:22 -0800611 }
612 }
613
Angus Kong9ef99252013-07-18 18:04:19 -0700614 private final class PostViewPictureCallback
615 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800616 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700617 public void onPictureTaken(byte [] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800618 mPostViewPictureCallbackTime = System.currentTimeMillis();
619 Log.v(TAG, "mShutterToPostViewCallbackTime = "
620 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
621 + "ms");
622 }
623 }
624
Angus Kong9ef99252013-07-18 18:04:19 -0700625 private final class RawPictureCallback
626 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800627 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700628 public void onPictureTaken(byte [] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800629 mRawPictureCallbackTime = System.currentTimeMillis();
630 Log.v(TAG, "mShutterToRawCallbackTime = "
631 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
632 }
633 }
634
Angus Kong9ef99252013-07-18 18:04:19 -0700635 private final class JpegPictureCallback
636 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800637 Location mLocation;
638
639 public JpegPictureCallback(Location loc) {
640 mLocation = loc;
641 }
642
643 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700644 public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
Sascha Haeberling88901942013-08-28 17:49:00 -0700645 mUI.enableShutter(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800646 if (mPaused) {
647 return;
648 }
Doris Liu6432cd62013-06-13 17:20:31 -0700649 if (mIsImageCaptureIntent) {
650 stopPreview();
651 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700652 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700653 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800654 }
655
656 mJpegPictureCallbackTime = System.currentTimeMillis();
657 // If postview callback has arrived, the captured image is displayed
658 // in postview callback. If not, the captured image is displayed in
659 // raw picture callback.
660 if (mPostViewPictureCallbackTime != 0) {
661 mShutterToPictureDisplayedTime =
662 mPostViewPictureCallbackTime - mShutterCallbackTime;
663 mPictureDisplayedToJpegCallbackTime =
664 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
665 } else {
666 mShutterToPictureDisplayedTime =
667 mRawPictureCallbackTime - mShutterCallbackTime;
668 mPictureDisplayedToJpegCallbackTime =
669 mJpegPictureCallbackTime - mRawPictureCallbackTime;
670 }
671 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
672 + mPictureDisplayedToJpegCallbackTime + "ms");
673
Michael Kolb8872c232013-01-29 10:33:22 -0800674 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
675 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700676 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800677 }
678
Doris Liu36e56fb2013-09-11 17:38:08 -0700679 ExifInterface exif = Exif.getExif(jpegData);
680 int orientation = Exif.getOrientation(exif);
Ruben Brunkd217ed02013-10-08 23:31:13 -0700681
Ruben Brunkd7488272013-10-10 18:45:53 -0700682 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800683 // Calculate the width and the height of the jpeg.
684 Size s = mParameters.getPictureSize();
Michael Kolb8872c232013-01-29 10:33:22 -0800685 int width, height;
686 if ((mJpegRotation + orientation) % 180 == 0) {
687 width = s.width;
688 height = s.height;
689 } else {
690 width = s.height;
691 height = s.width;
692 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700693 NamedEntity name = mNamedImages.getNextNameEntity();
694 String title = (name == null) ? null : name.title;
695 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -0700696
697 // Handle debug mode outputs
698 if (mDebugUri != null) {
699 // If using a debug uri, save jpeg there.
700 saveToDebugUri(jpegData);
701
702 // Adjust the title of the debug image shown in mediastore.
703 if (title != null) {
704 title = DEBUG_IMAGE_PREFIX + title;
705 }
706 }
707
Michael Kolb8872c232013-01-29 10:33:22 -0800708 if (title == null) {
709 Log.e(TAG, "Unbalanced name/data pair");
710 } else {
711 if (date == -1) date = mCaptureStartTime;
Angus Kong0d00a892013-03-26 11:40:40 -0700712 if (mHeading >= 0) {
713 // heading direction has been updated by the sensor.
714 ExifTag directionRefTag = exif.buildTag(
715 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
716 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
717 ExifTag directionTag = exif.buildTag(
718 ExifInterface.TAG_GPS_IMG_DIRECTION,
719 new Rational(mHeading, 1));
720 exif.setTag(directionRefTag);
721 exif.setTag(directionTag);
722 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800723 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -0800724 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700725 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800726 }
Doris Liuce2acbc2013-08-21 18:45:29 -0700727 // Animate capture with real jpeg data instead of a preview frame.
Doris Liu29da2db2013-08-28 14:28:45 -0700728 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800729 } else {
730 mJpegImageData = jpegData;
731 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -0700732 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800733 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800734 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800735 }
736 }
737
738 // Check this in advance of each shot so we don't add to shutter
739 // latency. It's true that someone else could write to the SD card in
740 // the mean time and fill it, but that could have happened between the
741 // shutter press and saving the JPEG too.
742 mActivity.updateStorageSpaceAndHint();
743
744 long now = System.currentTimeMillis();
745 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
746 Log.v(TAG, "mJpegCallbackFinishTime = "
747 + mJpegCallbackFinishTime + "ms");
748 mJpegPictureCallbackTime = 0;
749 }
750 }
751
Angus Kong9ef99252013-07-18 18:04:19 -0700752 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800753 @Override
754 public void onAutoFocus(
Angus Kong9ef99252013-07-18 18:04:19 -0700755 boolean focused, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800756 if (mPaused) return;
757
758 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
759 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
760 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800761 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800762 }
763 }
764
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700765 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -0800766 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700767 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800768 @Override
769 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700770 boolean moving, CameraProxy camera) {
771 mFocusManager.onAutoFocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -0800772 }
773 }
774
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700775 /**
776 * This class is just a thread-safe queue for name,date holder objects.
777 */
778 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800779 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -0800780
781 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700782 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -0800783 }
784
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700785 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -0800786 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700787 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -0800788 r.date = date;
789 mQueue.add(r);
790 }
791
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700792 public NamedEntity getNextNameEntity() {
793 synchronized(mQueue) {
794 if (!mQueue.isEmpty()) {
795 return mQueue.remove(0);
796 }
Michael Kolb8872c232013-01-29 10:33:22 -0800797 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700798 return null;
Michael Kolb8872c232013-01-29 10:33:22 -0800799 }
800
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700801 public static class NamedEntity {
802 public String title;
803 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -0800804 }
805 }
806
807 private void setCameraState(int state) {
808 mCameraState = state;
809 switch (state) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700810 case PhotoController.PREVIEW_STOPPED:
811 case PhotoController.SNAPSHOT_IN_PROGRESS:
812 case PhotoController.SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -0800813 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700814 break;
815 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -0800816 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700817 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800818 }
819 }
820
Sascha Haeberling37f36112013-08-06 14:31:52 -0700821 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800822 // Only animate when in full screen capture mode
823 // i.e. If monkey/a user swipes to the gallery during picture taking,
824 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700825 if (!mIsImageCaptureIntent) {
826 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700827 }
Michael Kolb8872c232013-01-29 10:33:22 -0800828 }
829
830 @Override
831 public boolean capture() {
832 // If we are already in the middle of taking a snapshot or the image save request
833 // is full then ignore.
834 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800835 || mCameraState == SWITCHING_CAMERA
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800836 || getServices().getMediaSaver() == null
837 || getServices().getMediaSaver().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800838 return false;
839 }
840 mCaptureStartTime = System.currentTimeMillis();
841 mPostViewPictureCallbackTime = 0;
842 mJpegImageData = null;
843
Angus Kongb50b5cb2013-08-09 14:55:20 -0700844 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -0800845
846 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700847 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -0800848 }
849
850 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800851 int orientation;
852 // We need to be consistent with the framework orientation (i.e. the
853 // orientation of the UI.) when the auto-rotate screen setting is on.
854 if (mActivity.isAutoRotateScreen()) {
855 orientation = (360 - mDisplayRotation) % 360;
856 } else {
857 orientation = mOrientation;
858 }
Angus Kong20fad242013-11-11 18:23:46 -0800859 mJpegRotation = CameraUtil.getJpegRotation(mActivity, mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800860 mParameters.setRotation(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -0800861 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700862 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -0800863 mCameraDevice.setParameters(mParameters);
864
Sascha Haeberling88901942013-08-28 17:49:00 -0700865 // We don't want user to press the button again while taking a
866 // multi-second HDR photo.
867 mUI.enableShutter(false);
Angus Kong9ef99252013-07-18 18:04:19 -0700868 mCameraDevice.takePicture(mHandler,
869 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -0700870 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -0700871 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -0800872
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700873 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -0800874
875 mFaceDetectionStarted = false;
876 setCameraState(SNAPSHOT_IN_PROGRESS);
Bobby Georgescu301b6462013-04-01 15:33:17 -0700877 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
Seth Raphaelcbd82672013-11-05 10:12:36 -0800878 UsageStatistics.ACTION_CAPTURE_DONE, "Photo", 0,
Seth Raphael44973262013-11-27 14:29:24 -0800879 UsageStatistics.hashFileName(mNamedImages.mQueue.lastElement().title + ".jpg"));
Michael Kolb8872c232013-01-29 10:33:22 -0800880 return true;
881 }
882
883 @Override
884 public void setFocusParameters() {
885 setCameraParameters(UPDATE_PARAM_PREFERENCE);
886 }
887
Michael Kolbd6954f32013-03-08 20:43:01 -0800888 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -0800889 // If scene mode is set, we cannot set flash mode, white balance, and
890 // focus mode, instead, we read it from driver
891 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
892 overrideCameraSettings(mParameters.getFlashMode(),
893 mParameters.getWhiteBalance(), mParameters.getFocusMode());
894 } else {
895 overrideCameraSettings(null, null, null);
896 }
897 }
898
899 private void overrideCameraSettings(final String flashMode,
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700900 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800901 mUI.overrideSettings(
902 CameraSettings.KEY_FLASH_MODE, flashMode,
903 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
904 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -0800905 }
906
Erin Dahlgren357b7672013-11-20 17:38:14 -0800907 private PreferenceGroup loadPreferenceGroup() {
Michael Kolb8872c232013-01-29 10:33:22 -0800908 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
Erin Dahlgren357b7672013-11-20 17:38:14 -0800909 mCameraId, CameraHolder.instance().getCameraInfo());
910 return settings.getPreferenceGroup(R.xml.camera_preferences);
Michael Kolb8872c232013-01-29 10:33:22 -0800911 }
912
913 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800914 public void onOrientationChanged(int orientation) {
915 // We keep the last known orientation. So if the user first orient
916 // the camera then point the camera to floor or sky, we still have
917 // the correct orientation.
918 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700919 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800920
921 // Show the toast after getting the first orientation changed.
Angus Kong13e87c42013-11-25 10:02:47 -0800922 if (mHandler.hasMessages(MSG_SHOW_TAP_TO_FOCUS_TOAST)) {
923 mHandler.removeMessages(MSG_SHOW_TAP_TO_FOCUS_TOAST);
Michael Kolb8872c232013-01-29 10:33:22 -0800924 showTapToFocusToast();
925 }
926 }
927
928 @Override
Angus Kong20fad242013-11-11 18:23:46 -0800929 public void onCameraAvailable(CameraProxy cameraProxy) {
930 if (mPaused) {
931 return;
932 }
933 mCameraDevice = cameraProxy;
934
Erin Dahlgren357b7672013-11-20 17:38:14 -0800935 resetExposureCompensation();
Angus Kong20fad242013-11-11 18:23:46 -0800936 initializeCapabilities();
937
938 // Reset zoom value index.
939 mZoomValue = 0;
940 if (mFocusManager == null) {
941 initializeFocusManager();
942 }
943 mFocusManager.setParameters(mInitialParams);
944
945 mParameters = mCameraDevice.getParameters();
946 setCameraParameters(UPDATE_PARAM_ALL);
947 mCameraPreviewParamsReady = true;
948 startPreview();
949
950 onCameraOpened();
951 }
952
953 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -0800954 public void onCaptureCancelled() {
955 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
956 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -0800957 }
958
Michael Kolbd6954f32013-03-08 20:43:01 -0800959 @Override
960 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -0800961 if (mPaused)
962 return;
Michael Kolbd6954f32013-03-08 20:43:01 -0800963 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -0800964 setupPreview();
965 }
966
Michael Kolbd6954f32013-03-08 20:43:01 -0800967 @Override
968 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -0800969 if (mPaused) {
970 return;
971 }
972
973 byte[] data = mJpegImageData;
974
975 if (mCropValue == null) {
976 // First handle the no crop case -- just return the value. If the
977 // caller specifies a "save uri" then write the data to its
978 // stream. Otherwise, pass back a scaled down version of the bitmap
979 // directly in the extras.
980 if (mSaveUri != null) {
981 OutputStream outputStream = null;
982 try {
983 outputStream = mContentResolver.openOutputStream(mSaveUri);
984 outputStream.write(data);
985 outputStream.close();
986
987 mActivity.setResultEx(Activity.RESULT_OK);
988 mActivity.finish();
989 } catch (IOException ex) {
990 // ignore exception
991 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700992 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -0800993 }
994 } else {
Angus Kong0d00a892013-03-26 11:40:40 -0700995 ExifInterface exif = Exif.getExif(data);
996 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -0700997 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
998 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800999 mActivity.setResultEx(Activity.RESULT_OK,
1000 new Intent("inline-data").putExtra("data", bitmap));
1001 mActivity.finish();
1002 }
1003 } else {
1004 // Save the image to a temp file and invoke the cropper
1005 Uri tempUri = null;
1006 FileOutputStream tempStream = null;
1007 try {
1008 File path = mActivity.getFileStreamPath(sTempCropFilename);
1009 path.delete();
1010 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1011 tempStream.write(data);
1012 tempStream.close();
1013 tempUri = Uri.fromFile(path);
1014 } catch (FileNotFoundException ex) {
1015 mActivity.setResultEx(Activity.RESULT_CANCELED);
1016 mActivity.finish();
1017 return;
1018 } catch (IOException ex) {
1019 mActivity.setResultEx(Activity.RESULT_CANCELED);
1020 mActivity.finish();
1021 return;
1022 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001023 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001024 }
1025
1026 Bundle newExtras = new Bundle();
1027 if (mCropValue.equals("circle")) {
1028 newExtras.putString("circleCrop", "true");
1029 }
1030 if (mSaveUri != null) {
1031 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1032 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001033 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001034 }
1035 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001036 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001037 }
1038
Sascha Haeberling37f36112013-08-06 14:31:52 -07001039 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001040 final String CROP_ACTION = "com.android.camera.action.CROP";
1041 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001042
1043 cropIntent.setData(tempUri);
1044 cropIntent.putExtras(newExtras);
1045
1046 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1047 }
1048 }
1049
Michael Kolb8872c232013-01-29 10:33:22 -08001050 @Override
1051 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001052 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001053 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1054 || (mCameraState == PREVIEW_STOPPED)) return;
1055
1056 // Do not do focus if there is not enough storage.
1057 if (pressed && !canTakePicture()) return;
1058
1059 if (pressed) {
Michael Kolb8872c232013-01-29 10:33:22 -08001060 mFocusManager.onShutterDown();
1061 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001062 // for countdown mode, we need to postpone the shutter release
1063 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001064 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001065 mFocusManager.onShutterUp();
1066 }
Michael Kolb8872c232013-01-29 10:33:22 -08001067 }
1068 }
1069
1070 @Override
1071 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001072 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001073 || (mCameraState == SWITCHING_CAMERA)
1074 || (mCameraState == PREVIEW_STOPPED)) return;
1075
1076 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001077 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001078 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001079 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001080 return;
1081 }
1082 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1083
Angus Kongb50b5cb2013-08-09 14:55:20 -07001084 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001085 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001086 }
Michael Kolb8872c232013-01-29 10:33:22 -08001087 // If the user wants to do a snapshot while the previous one is still
1088 // in progress, remember the fact and do it after we finish the previous
1089 // one and re-start the preview. Snapshot in progress also includes the
1090 // state that autofocus is focusing and a picture will be taken when
1091 // focus callback arrives.
1092 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1093 && !mIsImageCaptureIntent) {
1094 mSnapshotOnIdle = true;
1095 return;
1096 }
1097
Erin Dahlgren357b7672013-11-20 17:38:14 -08001098 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001099 String timer = settingsManager.get(SettingsManager.SETTING_TIMER);
1100 String playSound = settingsManager.get(SettingsManager.SETTING_TIMER_SOUND_EFFECTS);
Michael Kolb8872c232013-01-29 10:33:22 -08001101 int seconds = Integer.parseInt(timer);
1102 // When shutter button is pressed, check whether the previous countdown is
1103 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001104 if (mUI.isCountingDown()) {
1105 mUI.cancelCountDown();
1106 }
1107 if (seconds > 0) {
Erin Dahlgren357b7672013-11-20 17:38:14 -08001108 mUI.startCountDown(seconds, playSound.equals(SettingsManager.VALUE_ON));
Michael Kolb8872c232013-01-29 10:33:22 -08001109 } else {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001110 mSnapshotOnIdle = false;
1111 mFocusManager.doSnap();
Michael Kolb8872c232013-01-29 10:33:22 -08001112 }
1113 }
1114
Andy Huibersdef975d2013-11-22 09:13:39 -08001115 private void onResumeTasks() {
1116 Log.v(TAG, "Executing onResumeTasks.");
Michael Kolb8872c232013-01-29 10:33:22 -08001117 if (mOpenCameraFail || mCameraDisabled) return;
1118
Angus Kong20fad242013-11-11 18:23:46 -08001119 mActivity.getCameraProvider().requestCamera(mCameraId);
1120
Michael Kolb8872c232013-01-29 10:33:22 -08001121 mJpegPictureCallbackTime = 0;
1122 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001123
1124 mOnResumeTime = SystemClock.uptimeMillis();
1125 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001126
1127 // If first time initialization is not finished, put it in the
1128 // message queue.
1129 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001130 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001131 } else {
1132 initializeSecondTime();
1133 }
Michael Kolb8872c232013-01-29 10:33:22 -08001134
Bobby Georgescu0a7dd572013-03-12 22:45:17 -07001135 UsageStatistics.onContentViewChanged(
1136 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kong0d00a892013-03-26 11:40:40 -07001137
1138 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1139 if (gsensor != null) {
1140 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1141 }
1142
1143 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1144 if (msensor != null) {
1145 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1146 }
Michael Kolb8872c232013-01-29 10:33:22 -08001147 }
1148
Angus Kongc4e66562013-11-22 23:03:21 -08001149 /**
1150 * The focus manager is the first UI related element to get initialized,
1151 * and it requires the RenderOverlay, so initialize it here
1152 */
1153 private void initializeFocusManager() {
1154 // Create FocusManager object. startPreview needs it.
1155 // if mFocusManager not null, reuse it
1156 // otherwise create a new instance
1157 if (mFocusManager != null) {
1158 mFocusManager.removeMessages();
1159 } else {
1160 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
1161 mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
1162 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1163 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001164 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1165 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001166 mInitialParams, this, mMirror,
1167 mActivity.getMainLooper(), mUI);
1168 }
Angus Kong20fad242013-11-11 18:23:46 -08001169 }
1170
1171 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001172 public void resume() {
1173 mPaused = false;
1174 // Add delay on resume from lock screen only, in order to to speed up
1175 // the onResume --> onPause --> onResume cycle from lock screen.
1176 // Don't do always because letting go of thread can cause delay.
1177 String action = mActivity.getIntent().getAction();
1178 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1179 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1180 Log.v(TAG, "On resume, from lock screen.");
1181 // Note: onPauseAfterSuper() will delete this runnable, so we will
1182 // at most have 1 copy queued up.
1183 mHandler.postDelayed(new Runnable() {
Sameer Padala2c8cc452013-11-05 18:49:12 -08001184 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001185 public void run() {
1186 onResumeTasks();
1187 }
1188 }, ON_RESUME_TASKS_DELAY_MSEC);
1189 } else {
1190 Log.v(TAG, "On resume.");
1191 onResumeTasks();
1192 }
1193 }
1194
1195 @Override
1196 public void pause() {
Michael Kolb8872c232013-01-29 10:33:22 -08001197 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001198 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1199 if (gsensor != null) {
1200 mSensorManager.unregisterListener(this, gsensor);
1201 }
1202
1203 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1204 if (msensor != null) {
1205 mSensorManager.unregisterListener(this, msensor);
1206 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001207
Michael Kolb8872c232013-01-29 10:33:22 -08001208 // Reset the focus first. Camera CTS does not guarantee that
1209 // cancelAutoFocus is allowed after preview stops.
1210 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1211 mCameraDevice.cancelAutoFocus();
1212 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001213
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001214 // If the camera has not been opened asynchronously yet,
1215 // and startPreview hasn't been called, then this is a no-op.
1216 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001217 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001218
Angus Kongce5480e2013-01-29 17:43:48 -08001219 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001220
1221 if (mLocationManager != null) mLocationManager.recordLocation(false);
1222
1223 // If we are in an image capture intent and has taken
1224 // a picture, we just clear it in onPause.
1225 mJpegImageData = null;
1226
Angus Kongdcccc512013-08-08 17:06:03 -07001227 // Remove the messages and runnables in the queue.
1228 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001229
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001230 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001231 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001232 mUI.onPause();
1233
Michael Kolb8872c232013-01-29 10:33:22 -08001234 mPendingSwitchCameraId = -1;
1235 if (mFocusManager != null) mFocusManager.removeMessages();
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001236 MediaSaver s = getServices().getMediaSaver();
Angus Kong86d36312013-01-31 18:22:44 -08001237 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001238 s.setQueueListener(null);
Angus Kong86d36312013-01-31 18:22:44 -08001239 }
Michael Kolb8872c232013-01-29 10:33:22 -08001240 }
1241
Angus Kong20fad242013-11-11 18:23:46 -08001242 @Override
1243 public void destroy() {
1244 // TODO: implement this.
1245 }
1246
1247 @Override
1248 public void onPreviewSizeChanged(int width, int height) {
1249 // TODO: implement this.
1250 }
1251
1252 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001253 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001254 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001255 }
1256
1257 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001258 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001259 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001260 setDisplayOrientation();
1261 }
1262 }
1263
Michael Kolb8872c232013-01-29 10:33:22 -08001264 private boolean canTakePicture() {
Angus Kong2dcc0a92013-09-25 13:00:08 -07001265 return isCameraIdle() && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001266 }
1267
1268 @Override
1269 public void autoFocus() {
1270 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001271 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001272 setCameraState(FOCUSING);
1273 }
1274
1275 @Override
1276 public void cancelAutoFocus() {
1277 mCameraDevice.cancelAutoFocus();
1278 setCameraState(IDLE);
1279 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1280 }
1281
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001282 // Preview area is touched.
Michael Kolb8872c232013-01-29 10:33:22 -08001283 @Override
1284 public void onSingleTapUp(View view, int x, int y) {
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001285 if (mUI.isImmediateCapture()) {
1286 cancelAutoFocus();
1287 onShutterButtonClick();
1288 } else {
1289 onShutterButtonFocus(true);
1290 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001291 }
Michael Kolb8872c232013-01-29 10:33:22 -08001292 }
1293
1294 @Override
1295 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001296 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001297 }
1298
1299 @Override
1300 public boolean onKeyDown(int keyCode, KeyEvent event) {
1301 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001302 case KeyEvent.KEYCODE_VOLUME_UP:
1303 case KeyEvent.KEYCODE_VOLUME_DOWN:
1304 case KeyEvent.KEYCODE_FOCUS:
1305 if (/*TODO: mActivity.isInCameraApp() &&*/ mFirstTimeInitialized) {
1306 if (event.getRepeatCount() == 0) {
1307 onShutterButtonFocus(true);
1308 }
1309 return true;
1310 }
1311 return false;
1312 case KeyEvent.KEYCODE_CAMERA:
1313 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1314 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001315 }
1316 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001317 case KeyEvent.KEYCODE_DPAD_CENTER:
1318 // If we get a dpad center event without any focused view, move
1319 // the focus to the shutter button and press it.
1320 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1321 // Start auto-focus immediately to reduce shutter lag. After
1322 // the shutter button gets the focus, onShutterButtonFocus()
1323 // will be called again but it is fine.
1324 onShutterButtonFocus(true);
1325 mUI.pressShutterButton();
1326 }
1327 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001328 }
1329 return false;
1330 }
1331
1332 @Override
1333 public boolean onKeyUp(int keyCode, KeyEvent event) {
1334 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001335 case KeyEvent.KEYCODE_VOLUME_UP:
1336 case KeyEvent.KEYCODE_VOLUME_DOWN:
1337 if (/*mActivity.isInCameraApp() && */ mFirstTimeInitialized) {
1338 onShutterButtonClick();
1339 return true;
1340 }
1341 return false;
1342 case KeyEvent.KEYCODE_FOCUS:
1343 if (mFirstTimeInitialized) {
1344 onShutterButtonFocus(false);
1345 }
Michael Kolb8872c232013-01-29 10:33:22 -08001346 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001347 }
1348 return false;
1349 }
1350
Michael Kolb8872c232013-01-29 10:33:22 -08001351 private void closeCamera() {
1352 if (mCameraDevice != null) {
1353 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001354 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001355 mCameraDevice.setErrorCallback(null);
Ruben Brunk59147832013-11-05 15:53:28 -08001356
Michael Kolb8872c232013-01-29 10:33:22 -08001357 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001358 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001359 mCameraDevice = null;
1360 setCameraState(PREVIEW_STOPPED);
1361 mFocusManager.onCameraReleased();
1362 }
1363 }
1364
1365 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001366 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1367 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001368 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001369 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001370 if (mFocusManager != null) {
1371 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1372 }
Doris Liu6432cd62013-06-13 17:20:31 -07001373 // Change the camera display orientation
1374 if (mCameraDevice != null) {
1375 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1376 }
Michael Kolb8872c232013-01-29 10:33:22 -08001377 }
1378
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001379 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001380 private void setupPreview() {
1381 mFocusManager.resetTouchFocus();
1382 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001383 }
1384
Angus Kong20fad242013-11-11 18:23:46 -08001385 /**
1386 * Returns whether we can/should start the preview or not.
1387 */
1388 private boolean checkPreviewPreconditions() {
1389 if (mPaused) {
1390 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001391 }
Michael Kolb8872c232013-01-29 10:33:22 -08001392
Angus Kong20fad242013-11-11 18:23:46 -08001393 if (mCameraDevice == null) {
1394 Log.w(TAG, "startPreview: camera device not ready yet.");
1395 return false;
1396 }
1397
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001398 SurfaceTexture st = mUI.getSurfaceTexture();
1399 if (st == null) {
1400 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001401 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001402 }
1403
1404 if (!mCameraPreviewParamsReady) {
1405 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001406 return false;
1407 }
1408 return true;
1409 }
1410
1411 /**
1412 * The start/stop preview should only run on the UI thread.
1413 */
1414 private void startPreview() {
1415 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001416 return;
1417 }
Angus Kong20fad242013-11-11 18:23:46 -08001418
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001419 mCameraDevice.setErrorCallback(mErrorCallback);
1420 // ICS camera frameworks has a bug. Face detection state is not cleared 1589
1421 // after taking a picture. Stop the preview to work around it. The bug
1422 // was fixed in JB.
1423 if (mCameraState != PREVIEW_STOPPED) {
1424 stopPreview();
1425 }
1426
1427 setDisplayOrientation();
1428
1429 if (!mSnapshotOnIdle) {
1430 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1431 // resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001432 String focusMode = mFocusManager.getFocusMode();
1433 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001434 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001435 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001436 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1437 }
1438 setCameraParameters(UPDATE_PARAM_ALL);
1439 // Let UI set its expected aspect ratio
Angus Kong20fad242013-11-11 18:23:46 -08001440 mCameraDevice.setPreviewTexture(mUI.getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001441
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001442 Log.v(TAG, "startPreview");
1443 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001444
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001445 mFocusManager.onPreviewStarted();
1446 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -08001447
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001448 if (mSnapshotOnIdle) {
1449 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001450 }
1451 }
1452
Michael Kolbd6954f32013-03-08 20:43:01 -08001453 @Override
1454 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001455 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1456 Log.v(TAG, "stopPreview");
1457 mCameraDevice.stopPreview();
1458 mFaceDetectionStarted = false;
1459 }
1460 setCameraState(PREVIEW_STOPPED);
1461 if (mFocusManager != null) mFocusManager.onPreviewStopped();
Sameer Padala2c8cc452013-11-05 18:49:12 -08001462 stopSmartCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001463 }
1464
1465 @SuppressWarnings("deprecation")
1466 private void updateCameraParametersInitialize() {
1467 // Reset preview frame rate to the maximum because it may be lowered by
1468 // video camera application.
ztenghui16a35202013-09-23 11:35:36 -07001469 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters);
1470 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001471 mParameters.setPreviewFpsRange(
1472 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1473 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001474 }
1475
Angus Kongb50b5cb2013-08-09 14:55:20 -07001476 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001477
1478 // Disable video stabilization. Convenience methods not available in API
1479 // level <= 14
1480 String vstabSupported = mParameters.get("video-stabilization-supported");
1481 if ("true".equals(vstabSupported)) {
1482 mParameters.set("video-stabilization", "false");
1483 }
1484 }
1485
1486 private void updateCameraParametersZoom() {
1487 // Set zoom.
1488 if (mParameters.isZoomSupported()) {
1489 mParameters.setZoom(mZoomValue);
1490 }
1491 }
1492
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001493 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001494 private void setAutoExposureLockIfSupported() {
1495 if (mAeLockSupported) {
1496 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1497 }
1498 }
1499
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001500 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001501 private void setAutoWhiteBalanceLockIfSupported() {
1502 if (mAwbLockSupported) {
1503 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1504 }
1505 }
1506
Michael Kolb8872c232013-01-29 10:33:22 -08001507 private void setFocusAreasIfSupported() {
1508 if (mFocusAreaSupported) {
1509 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1510 }
1511 }
1512
Michael Kolb8872c232013-01-29 10:33:22 -08001513 private void setMeteringAreasIfSupported() {
1514 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001515 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1516 }
1517 }
1518
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001519 private boolean updateCameraParametersPreference() {
Erin Dahlgren357b7672013-11-20 17:38:14 -08001520 SettingsManager settingsManager = mActivity.getSettingsManager();
1521
Michael Kolb8872c232013-01-29 10:33:22 -08001522 setAutoExposureLockIfSupported();
1523 setAutoWhiteBalanceLockIfSupported();
1524 setFocusAreasIfSupported();
1525 setMeteringAreasIfSupported();
1526
1527 // Set picture size.
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001528 String pictureSize = settingsManager.get(SettingsManager.SETTING_PICTURE_SIZE);
Michael Kolb8872c232013-01-29 10:33:22 -08001529 if (pictureSize == null) {
Erin Dahlgren357b7672013-11-20 17:38:14 -08001530 //TODO: deprecate CameraSettings.
1531 CameraSettings.initialCameraPictureSize(
1532 mActivity, mParameters, settingsManager);
Michael Kolb8872c232013-01-29 10:33:22 -08001533 } else {
1534 List<Size> supported = mParameters.getSupportedPictureSizes();
1535 CameraSettings.setCameraPictureSize(
1536 pictureSize, supported, mParameters);
1537 }
1538 Size size = mParameters.getPictureSize();
1539
1540 // Set a preview size that is closest to the viewfinder height and has
1541 // the right aspect ratio.
1542 List<Size> sizes = mParameters.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001543 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Michael Kolb8872c232013-01-29 10:33:22 -08001544 (double) size.width / size.height);
1545 Size original = mParameters.getPreviewSize();
1546 if (!original.equals(optimalSize)) {
1547 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
Doris Liu6432cd62013-06-13 17:20:31 -07001548
Michael Kolb8872c232013-01-29 10:33:22 -08001549 // Zoom related settings will be changed for different preview
1550 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001551 if (mHandler.getLooper() == Looper.myLooper()) {
1552 // On UI thread only, not when camera starts up
1553 setupPreview();
1554 } else {
1555 mCameraDevice.setParameters(mParameters);
1556 }
Michael Kolb8872c232013-01-29 10:33:22 -08001557 mParameters = mCameraDevice.getParameters();
1558 }
Doris Liu95405742013-11-05 15:25:26 -08001559
1560 if(optimalSize.width != 0 && optimalSize.height != 0) {
1561 mUI.updatePreviewAspectRatio((float) optimalSize.width
1562 / (float) optimalSize.height);
1563 }
Michael Kolb8872c232013-01-29 10:33:22 -08001564 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1565
1566 // Since changing scene mode may change supported values, set scene mode
1567 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1568 // separate preference.
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001569 String onValue = mActivity.getString(R.string.setting_on_value);
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001570 String hdr = settingsManager.get(SettingsManager.SETTING_CAMERA_HDR);
1571 String hdrPlus = settingsManager.get(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001572 boolean hdrOn = onValue.equals(hdr);
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001573 boolean hdrPlusOn = onValue.equals(hdrPlus);
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001574
1575 boolean doGcamModeSwitch = false;
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001576 if (hdrPlusOn && GcamHelper.hasGcamCapture()) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001577 // Kick off mode switch to gcam.
1578 doGcamModeSwitch = true;
Michael Kolb8872c232013-01-29 10:33:22 -08001579 } else {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001580 if (hdrOn) {
1581 mSceneMode = CameraUtil.SCENE_MODE_HDR;
1582 } else {
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001583 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001584 }
Michael Kolb8872c232013-01-29 10:33:22 -08001585 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001586 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
Michael Kolb8872c232013-01-29 10:33:22 -08001587 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1588 mParameters.setSceneMode(mSceneMode);
1589
1590 // Setting scene mode will change the settings of flash mode,
1591 // white balance, and focus mode. Here we read back the
1592 // parameters, so we can know those settings.
1593 mCameraDevice.setParameters(mParameters);
1594 mParameters = mCameraDevice.getParameters();
1595 }
1596 } else {
1597 mSceneMode = mParameters.getSceneMode();
1598 if (mSceneMode == null) {
1599 mSceneMode = Parameters.SCENE_MODE_AUTO;
1600 }
1601 }
1602
1603 // Set JPEG quality.
1604 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1605 CameraProfile.QUALITY_HIGH);
1606 mParameters.setJpegQuality(jpegQuality);
1607
1608 // For the following settings, we need to check if the settings are
1609 // still supported by latest driver, if not, ignore the settings.
1610
1611 // Set exposure compensation
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001612 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE));
Michael Kolb8872c232013-01-29 10:33:22 -08001613 int max = mParameters.getMaxExposureCompensation();
1614 int min = mParameters.getMinExposureCompensation();
1615 if (value >= min && value <= max) {
1616 mParameters.setExposureCompensation(value);
1617 } else {
1618 Log.w(TAG, "invalid exposure range: " + value);
1619 }
1620
1621 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1622 // Set flash mode.
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001623 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
Michael Kolb8872c232013-01-29 10:33:22 -08001624 List<String> supportedFlash = mParameters.getSupportedFlashModes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001625 if (CameraUtil.isSupported(flashMode, supportedFlash)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001626 mParameters.setFlashMode(flashMode);
1627 } else {
1628 flashMode = mParameters.getFlashMode();
1629 if (flashMode == null) {
1630 flashMode = mActivity.getString(
1631 R.string.pref_camera_flashmode_no_flash);
1632 }
1633 }
1634
1635 // Set white balance parameter.
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001636 String whiteBalance = settingsManager.get(SettingsManager.SETTING_WHITE_BALANCE);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001637 if (CameraUtil.isSupported(whiteBalance,
Michael Kolb8872c232013-01-29 10:33:22 -08001638 mParameters.getSupportedWhiteBalance())) {
1639 mParameters.setWhiteBalance(whiteBalance);
1640 } else {
1641 whiteBalance = mParameters.getWhiteBalance();
1642 if (whiteBalance == null) {
1643 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1644 }
1645 }
1646
1647 // Set focus mode.
1648 mFocusManager.overrideFocusMode(null);
1649 mParameters.setFocusMode(mFocusManager.getFocusMode());
1650 } else {
1651 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1652 }
1653
Angus Kongdcccc512013-08-08 17:06:03 -07001654 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
Michael Kolb8872c232013-01-29 10:33:22 -08001655 updateAutoFocusMoveCallback();
1656 }
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001657
1658 return doGcamModeSwitch;
Michael Kolb8872c232013-01-29 10:33:22 -08001659 }
1660
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001661 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001662 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001663 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07001664 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07001665 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001666 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07001667 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001668 }
1669 }
1670
1671 // We separate the parameters into several subsets, so we can update only
1672 // the subsets actually need updating. The PREFERENCE set needs extra
1673 // locking because the preference can be changed from GLThread as well.
1674 private void setCameraParameters(int updateSet) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001675 boolean doModeSwitch = false;
1676
Michael Kolb8872c232013-01-29 10:33:22 -08001677 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1678 updateCameraParametersInitialize();
1679 }
1680
1681 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1682 updateCameraParametersZoom();
1683 }
1684
1685 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001686 doModeSwitch = updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08001687 }
1688
1689 mCameraDevice.setParameters(mParameters);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001690
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001691 // Switch to gcam module if HDR+ was selected
Angus Kong0fb819b2013-10-08 13:44:19 -07001692 if (doModeSwitch && !mIsImageCaptureIntent) {
Angus Kong13e87c42013-11-25 10:02:47 -08001693 mHandler.sendEmptyMessage(MSG_SWITCH_TO_GCAM_MODULE);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001694 }
Michael Kolb8872c232013-01-29 10:33:22 -08001695 }
1696
1697 // If the Camera is idle, update the parameters immediately, otherwise
1698 // accumulate them in mUpdateSet and update later.
1699 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1700 mUpdateSet |= additionalUpdateSet;
1701 if (mCameraDevice == null) {
1702 // We will update all the parameters when we open the device, so
1703 // we don't need to do anything now.
1704 mUpdateSet = 0;
1705 return;
1706 } else if (isCameraIdle()) {
1707 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001708 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001709 mUpdateSet = 0;
1710 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08001711 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1712 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08001713 }
1714 }
1715 }
1716
ztenghui7b265a62013-09-09 14:58:44 -07001717 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001718 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001719 return (mCameraState == IDLE) ||
1720 (mCameraState == PREVIEW_STOPPED) ||
1721 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1722 && (mCameraState != SWITCHING_CAMERA));
1723 }
1724
ztenghui7b265a62013-09-09 14:58:44 -07001725 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001726 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001727 String action = mActivity.getIntent().getAction();
1728 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Angus Kong6a8e8a12013-07-19 14:55:07 -07001729 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08001730 }
1731
1732 private void setupCaptureParams() {
1733 Bundle myExtras = mActivity.getIntent().getExtras();
1734 if (myExtras != null) {
1735 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1736 mCropValue = myExtras.getString("crop");
1737 }
1738 }
1739
Michael Kolb8872c232013-01-29 10:33:22 -08001740 @Override
1741 public void onSharedPreferenceChanged() {
1742 // ignore the events after "onPause()"
1743 if (mPaused) return;
1744
Erin Dahlgren357b7672013-11-20 17:38:14 -08001745 SettingsController settingsController = mActivity.getSettingsController();
1746 settingsController.syncLocationManager();
Michael Kolb8872c232013-01-29 10:33:22 -08001747
1748 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolb8872c232013-01-29 10:33:22 -08001749 }
1750
1751 @Override
1752 public void onCameraPickerClicked(int cameraId) {
1753 if (mPaused || mPendingSwitchCameraId != -1) return;
1754
1755 mPendingSwitchCameraId = cameraId;
Doris Liu6432cd62013-06-13 17:20:31 -07001756
1757 Log.v(TAG, "Start to switch camera. cameraId=" + cameraId);
1758 // We need to keep a preview frame for the animation before
1759 // releasing the camera. This will trigger onPreviewTextureCopied.
1760 //TODO: Need to animate the camera switch
1761 switchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001762 }
1763
Michael Kolb8872c232013-01-29 10:33:22 -08001764 @Override
1765 public void onOverriddenPreferencesClicked() {
1766 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001767 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001768 }
1769
1770 private void showTapToFocusToast() {
1771 // TODO: Use a toast?
1772 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1773 // Clear the preference.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001774 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -08001775 settingsManager.setBoolean(
1776 SettingsManager.SETTING_CAMERA_FIRST_USE_HINT_SHOWN, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001777 }
1778
1779 private void initializeCapabilities() {
1780 mInitialParams = mCameraDevice.getParameters();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001781 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
1782 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
1783 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams);
1784 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams);
Angus Kongdcccc512013-08-08 17:06:03 -07001785 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07001786 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08001787 }
1788
Michael Kolb8872c232013-01-29 10:33:22 -08001789 @Override
1790 public void onCountDownFinished() {
1791 mSnapshotOnIdle = false;
1792 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08001793 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08001794 }
1795
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001796 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08001797 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001798 public int onZoomChanged(int index) {
1799 // Not useful to change zoom value when the activity is paused.
1800 if (mPaused) return index;
1801 mZoomValue = index;
1802 if (mParameters == null || mCameraDevice == null) return index;
1803 // Set zoom parameters asynchronously
1804 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07001805 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08001806 Parameters p = mCameraDevice.getParameters();
1807 if (p != null) return p.getZoom();
1808 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001809 }
1810
1811 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001812 public int getCameraState() {
1813 return mCameraState;
1814 }
1815
1816 @Override
1817 public void onQueueStatus(boolean full) {
1818 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08001819 }
Angus Kong86d36312013-01-31 18:22:44 -08001820
1821 @Override
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001822 public void onMediaSaverAvailable(MediaSaver s) {
Angus Kong86d36312013-01-31 18:22:44 -08001823 // We set the listener only when both service and shutterbutton
1824 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08001825 if (mFirstTimeInitialized) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001826 s.setQueueListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -08001827 }
Angus Kong86d36312013-01-31 18:22:44 -08001828 }
Angus Kong0d00a892013-03-26 11:40:40 -07001829
1830 @Override
1831 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1832 }
1833
1834 @Override
1835 public void onSensorChanged(SensorEvent event) {
1836 int type = event.sensor.getType();
1837 float[] data;
1838 if (type == Sensor.TYPE_ACCELEROMETER) {
1839 data = mGData;
1840 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1841 data = mMData;
1842 } else {
1843 // we should not be here.
1844 return;
1845 }
1846 for (int i = 0; i < 3 ; i++) {
1847 data[i] = event.values[i];
1848 }
1849 float[] orientation = new float[3];
1850 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
1851 SensorManager.getOrientation(mR, orientation);
1852 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
1853 if (mHeading < 0) {
1854 mHeading += 360;
1855 }
Angus Kong0d00a892013-03-26 11:40:40 -07001856 }
Doris Liu6432cd62013-06-13 17:20:31 -07001857
1858 @Override
ztenghui7b265a62013-09-09 14:58:44 -07001859 public void onPreviewFocusChanged(boolean previewFocused) {
1860 mUI.onPreviewFocusChanged(previewFocused);
Doris Liu6432cd62013-06-13 17:20:31 -07001861 }
1862
Erin Dahlgren3044d8c2013-10-10 18:23:45 -07001863 @Override
1864 public boolean arePreviewControlsVisible() {
Marco Nelissen0744e4a2013-11-22 01:47:37 +00001865 return false;
Erin Dahlgren3044d8c2013-10-10 18:23:45 -07001866 }
1867
Ruben Brunkd217ed02013-10-08 23:31:13 -07001868 // For debugging only.
1869 public void setDebugUri(Uri uri) {
1870 mDebugUri = uri;
1871 }
1872
1873 // For debugging only.
1874 private void saveToDebugUri(byte[] data) {
1875 if (mDebugUri != null) {
1876 OutputStream outputStream = null;
1877 try {
1878 outputStream = mContentResolver.openOutputStream(mDebugUri);
1879 outputStream.write(data);
1880 outputStream.close();
1881 } catch (IOException e) {
1882 Log.e(TAG, "Exception while writing debug jpeg file", e);
1883 } finally {
1884 CameraUtil.closeSilently(outputStream);
1885 }
1886 }
1887 }
1888
Doris Liu6432cd62013-06-13 17:20:31 -07001889/* Below is no longer needed, except to get rid of compile error
1890 * TODO: Remove these
1891 */
1892
1893 // TODO: Delete this function after old camera code is removed
1894 @Override
1895 public void onRestorePreferencesClicked() {}
1896
Michael Kolb8872c232013-01-29 10:33:22 -08001897}