blob: 67bd698f19976e8f2fe72cf19946c0d540d4caa4 [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.ContentProviderClient;
22import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070023import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080024import android.content.Intent;
25import android.content.SharedPreferences.Editor;
26import android.content.res.Configuration;
27import android.graphics.Bitmap;
Doris Liu36ebcb12013-10-28 14:44:24 -070028import android.graphics.Rect;
Michael Kolb8872c232013-01-29 10:33:22 -080029import android.graphics.SurfaceTexture;
30import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080031import android.hardware.Camera.Parameters;
Michael Kolb8872c232013-01-29 10:33:22 -080032import android.hardware.Camera.Size;
Angus Kong0d00a892013-03-26 11:40:40 -070033import android.hardware.Sensor;
34import android.hardware.SensorEvent;
35import android.hardware.SensorEventListener;
36import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.location.Location;
38import android.media.CameraProfile;
39import android.net.Uri;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070040import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080041import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080042import android.os.Handler;
43import android.os.Looper;
44import android.os.Message;
45import android.os.MessageQueue;
46import android.os.SystemClock;
47import android.provider.MediaStore;
48import android.util.Log;
Michael Kolb8872c232013-01-29 10:33:22 -080049import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080050import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080051import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080052import android.view.WindowManager;
Michael Kolb8872c232013-01-29 10:33:22 -080053
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;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070059import com.android.camera.PhotoModule.NamedImages.NamedEntity;
Angus Kong20fad242013-11-11 18:23:46 -080060import com.android.camera.app.AppController;
Angus Kongfd4fc0e2013-11-07 15:38:09 -080061import com.android.camera.app.MediaSaver;
ztenghuia16e7b52013-08-23 11:47:56 -070062import com.android.camera.exif.ExifInterface;
63import com.android.camera.exif.ExifTag;
64import com.android.camera.exif.Rational;
Angus Kong20fad242013-11-11 18:23:46 -080065import com.android.camera.module.ModuleController;
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;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070072import com.android.camera.util.UsageStatistics;
73import com.android.camera2.R;
Michael Kolb8872c232013-01-29 10:33:22 -080074
Angus Kongdcccc512013-08-08 17:06:03 -070075import java.io.File;
76import java.io.FileNotFoundException;
77import java.io.FileOutputStream;
78import java.io.IOException;
79import java.io.OutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070080import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070081import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070082
Michael Kolb8872c232013-01-29 10:33:22 -080083public class PhotoModule
Angus Kong20fad242013-11-11 18:23:46 -080084 implements CameraModule, ModuleController, PhotoController, FocusOverlayManager.Listener,
85 CameraPreference.OnPreferenceChangedListener, ShutterButton.OnShutterButtonListener,
86 MediaSaver.QueueListener, OnCountDownFinishedListener, SensorEventListener {
Michael Kolb8872c232013-01-29 10:33:22 -080087
88 private static final String TAG = "CAM_PhotoModule";
89
90 // We number the request code from 1000 to avoid collision with Gallery.
91 private static final int REQUEST_CROP = 1000;
92
93 private static final int SETUP_PREVIEW = 1;
94 private static final int FIRST_TIME_INIT = 2;
95 private static final int CLEAR_SCREEN_DELAY = 3;
96 private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
Angus Kongdcccc512013-08-08 17:06:03 -070097 private static final int SHOW_TAP_TO_FOCUS_TOAST = 5;
98 private static final int SWITCH_CAMERA = 6;
99 private static final int SWITCH_CAMERA_START_ANIMATION = 7;
100 private static final int CAMERA_OPEN_DONE = 8;
101 private static final int OPEN_CAMERA_FAIL = 9;
102 private static final int CAMERA_DISABLED = 10;
ztenghui367c7c82013-10-16 14:43:26 -0700103 private static final int SWITCH_TO_GCAM_MODULE = 11;
Michael Kolb8872c232013-01-29 10:33:22 -0800104
105 // The subset of parameters we need to update in setCameraParameters().
106 private static final int UPDATE_PARAM_INITIALIZE = 1;
107 private static final int UPDATE_PARAM_ZOOM = 2;
108 private static final int UPDATE_PARAM_PREFERENCE = 4;
109 private static final int UPDATE_PARAM_ALL = -1;
110
111 // This is the timeout to keep the camera in onPause for the first time
112 // after screen on if the activity is started from secure lock screen.
113 private static final int KEEP_CAMERA_TIMEOUT = 1000; // ms
114
Ruben Brunkd7488272013-10-10 18:45:53 -0700115 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
116
Michael Kolb8872c232013-01-29 10:33:22 -0800117 // copied from Camera hierarchy
118 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800119 private CameraProxy mCameraDevice;
120 private int mCameraId;
121 private Parameters mParameters;
122 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800123
124 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800125
Michael Kolb8872c232013-01-29 10:33:22 -0800126 // The activity is going to switch to the specified camera id. This is
127 // needed because texture copy is done in GL thread. -1 means camera is not
128 // switching.
129 protected int mPendingSwitchCameraId = -1;
130 private boolean mOpenCameraFail;
131 private boolean mCameraDisabled;
132
133 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
134 // needed to be updated in mUpdateSet.
135 private int mUpdateSet;
136
137 private static final int SCREEN_DELAY = 2 * 60 * 1000;
138
139 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800140
141 private Parameters mInitialParams;
142 private boolean mFocusAreaSupported;
143 private boolean mMeteringAreaSupported;
144 private boolean mAeLockSupported;
145 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700146 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800147
148 // The degrees of the device rotated clockwise from its natural orientation.
149 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
150 private ComboPreferences mPreferences;
151
152 private static final String sTempCropFilename = "crop-temp";
153
154 private ContentProviderClient mMediaProviderClient;
Michael Kolb8872c232013-01-29 10:33:22 -0800155 private boolean mFaceDetectionStarted = false;
156
Michael Kolb8872c232013-01-29 10:33:22 -0800157 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
158 private String mCropValue;
159 private Uri mSaveUri;
160
Ruben Brunkd217ed02013-10-08 23:31:13 -0700161 private Uri mDebugUri;
162
Angus Kongce5480e2013-01-29 17:43:48 -0800163 // We use a queue to generated names of the images to be used later
164 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800165 private NamedImages mNamedImages;
166
167 private Runnable mDoSnapRunnable = new Runnable() {
168 @Override
169 public void run() {
170 onShutterButtonClick();
171 }
172 };
173
Michael Kolb8872c232013-01-29 10:33:22 -0800174 /**
175 * An unpublished intent flag requesting to return as soon as capturing
176 * is completed.
177 *
178 * TODO: consider publishing by moving into MediaStore.
179 */
180 private static final String EXTRA_QUICK_CAPTURE =
181 "android.intent.extra.quickCapture";
182
183 // The display rotation in degrees. This is only valid when mCameraState is
184 // not PREVIEW_STOPPED.
185 private int mDisplayRotation;
186 // The value for android.hardware.Camera.setDisplayOrientation.
187 private int mCameraDisplayOrientation;
188 // The value for UI components like indicators.
189 private int mDisplayOrientation;
190 // The value for android.hardware.Camera.Parameters.setRotation.
191 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700192 // Indicates whether we are using front camera
193 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800194 private boolean mFirstTimeInitialized;
195 private boolean mIsImageCaptureIntent;
196
Michael Kolb8872c232013-01-29 10:33:22 -0800197 private int mCameraState = PREVIEW_STOPPED;
198 private boolean mSnapshotOnIdle = false;
199
200 private ContentResolver mContentResolver;
201
202 private LocationManager mLocationManager;
203
Michael Kolb8872c232013-01-29 10:33:22 -0800204 private final PostViewPictureCallback mPostViewPictureCallback =
205 new PostViewPictureCallback();
206 private final RawPictureCallback mRawPictureCallback =
207 new RawPictureCallback();
208 private final AutoFocusCallback mAutoFocusCallback =
209 new AutoFocusCallback();
210 private final Object mAutoFocusMoveCallback =
211 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700212 ? new AutoFocusMoveCallback()
213 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800214
215 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
216
217 private long mFocusStartTime;
218 private long mShutterCallbackTime;
219 private long mPostViewPictureCallbackTime;
220 private long mRawPictureCallbackTime;
221 private long mJpegPictureCallbackTime;
222 private long mOnResumeTime;
223 private byte[] mJpegImageData;
224
225 // These latency time are for the CameraLatency test.
226 public long mAutoFocusTime;
227 public long mShutterLag;
228 public long mShutterToPictureDisplayedTime;
229 public long mPictureDisplayedToJpegCallbackTime;
230 public long mJpegCallbackFinishTime;
231 public long mCaptureStartTime;
232
233 // This handles everything about focus.
234 private FocusOverlayManager mFocusManager;
235
Michael Kolb8872c232013-01-29 10:33:22 -0800236 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800237
238 private final Handler mHandler = new MainHandler();
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800239
Michael Kolb8872c232013-01-29 10:33:22 -0800240 private PreferenceGroup mPreferenceGroup;
241
242 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700243 private SensorManager mSensorManager;
244 private float[] mGData = new float[3];
245 private float[] mMData = new float[3];
246 private float[] mR = new float[16];
247 private int mHeading = -1;
248
Angus Kongdcccc512013-08-08 17:06:03 -0700249 // True if all the parameters needed to start preview is ready.
250 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700251
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800252 private MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
253 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800254 @Override
255 public void onMediaSaved(Uri uri) {
256 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700257 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800258 }
259 }
260 };
Michael Kolb8872c232013-01-29 10:33:22 -0800261
Angus Kongdcccc512013-08-08 17:06:03 -0700262 private void checkDisplayRotation() {
263 // Set the display orientation if display rotation has changed.
264 // Sometimes this happens when the device is held upside
265 // down and camera app is opened. Rotation animation will
266 // take some time and the rotation value we have got may be
267 // wrong. Framework does not have a callback for this now.
268 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
269 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800270 }
Angus Kongdcccc512013-08-08 17:06:03 -0700271 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
272 mHandler.postDelayed(new Runnable() {
273 @Override
274 public void run() {
275 checkDisplayRotation();
276 }
277 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800278 }
279 }
280
281 /**
282 * This Handler is used to post message back onto the main thread of the
283 * application
284 */
285 private class MainHandler extends Handler {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800286 public MainHandler() {
287 super(Looper.getMainLooper());
288 }
289
Michael Kolb8872c232013-01-29 10:33:22 -0800290 @Override
291 public void handleMessage(Message msg) {
292 switch (msg.what) {
293 case SETUP_PREVIEW: {
294 setupPreview();
295 break;
296 }
297
298 case CLEAR_SCREEN_DELAY: {
299 mActivity.getWindow().clearFlags(
300 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
301 break;
302 }
303
304 case FIRST_TIME_INIT: {
305 initializeFirstTime();
306 break;
307 }
308
309 case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
310 setCameraParametersWhenIdle(0);
311 break;
312 }
313
Michael Kolb8872c232013-01-29 10:33:22 -0800314 case SHOW_TAP_TO_FOCUS_TOAST: {
315 showTapToFocusToast();
316 break;
317 }
318
319 case SWITCH_CAMERA: {
320 switchCamera();
321 break;
322 }
323
324 case SWITCH_CAMERA_START_ANIMATION: {
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700325 // TODO: Need to revisit
326 // ((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -0800327 break;
328 }
329
330 case CAMERA_OPEN_DONE: {
Michael Kolbd6954f32013-03-08 20:43:01 -0800331 onCameraOpened();
Michael Kolb8872c232013-01-29 10:33:22 -0800332 break;
333 }
334
Michael Kolb8872c232013-01-29 10:33:22 -0800335 case OPEN_CAMERA_FAIL: {
Michael Kolb8872c232013-01-29 10:33:22 -0800336 mOpenCameraFail = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700337 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800338 R.string.cannot_connect_camera);
339 break;
340 }
341
342 case CAMERA_DISABLED: {
Michael Kolb8872c232013-01-29 10:33:22 -0800343 mCameraDisabled = true;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700344 CameraUtil.showErrorAndFinish(mActivity,
Michael Kolb8872c232013-01-29 10:33:22 -0800345 R.string.camera_disabled);
346 break;
347 }
ztenghui367c7c82013-10-16 14:43:26 -0700348
349 case SWITCH_TO_GCAM_MODULE: {
Erin Dahlgrend7b8cb52013-11-14 17:25:37 -0800350 mActivity.onModeSelected(ModeListView.MODE_GCAM);
ztenghui367c7c82013-10-16 14:43:26 -0700351 }
Michael Kolb8872c232013-01-29 10:33:22 -0800352 }
353 }
354 }
355
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700356
Michael Kolb8872c232013-01-29 10:33:22 -0800357 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700358 public void init(CameraActivity activity, View parent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800359 mActivity = activity;
Michael Kolbd6954f32013-03-08 20:43:01 -0800360 mUI = new PhotoUI(activity, this, parent);
Michael Kolb8872c232013-01-29 10:33:22 -0800361 mPreferences = new ComboPreferences(mActivity);
362 CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal());
363 mCameraId = getPreferredCameraId(mPreferences);
364
365 mContentResolver = mActivity.getContentResolver();
366
Michael Kolb8872c232013-01-29 10:33:22 -0800367 // Surface texture is from camera screen nail and startPreview needs it.
368 // This must be done before startPreview.
369 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800370
371 mPreferences.setLocalId(mActivity, mCameraId);
372 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
Angus Kong20fad242013-11-11 18:23:46 -0800373
374 mActivity.getCameraProvider().requestCamera(mCameraId);
375
Michael Kolb8872c232013-01-29 10:33:22 -0800376 // we need to reset exposure for the preview
377 resetExposureCompensation();
Michael Kolb8872c232013-01-29 10:33:22 -0800378
379 initializeControlByIntent();
380 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Michael Kolbd6954f32013-03-08 20:43:01 -0800381 mLocationManager = new LocationManager(mActivity, mUI);
Angus Kong0d00a892013-03-26 11:40:40 -0700382 mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE));
Michael Kolbd6954f32013-03-08 20:43:01 -0800383 }
384
385 private void initializeControlByIntent() {
386 mUI.initializeControlByIntent();
387 if (mIsImageCaptureIntent) {
388 setupCaptureParams();
389 }
390 }
391
392 private void onPreviewStarted() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800393 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800394 startFaceDetection();
395 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() {
401 if (RecordLocationPreference.isSet(mPreferences)) {
402 return;
403 }
404 if (mActivity.isSecureCamera()) return;
405 // Check if the back camera exists
406 int backCameraId = CameraHolder.instance().getBackCameraId();
407 if (backCameraId == -1) {
408 // If there is no back camera, do not show the prompt.
409 return;
410 }
Doris Liu6a83d522013-07-02 12:03:32 -0700411 mUI.showLocationDialog();
412 }
Michael Kolb8872c232013-01-29 10:33:22 -0800413
ztenghui7b265a62013-09-09 14:58:44 -0700414 @Override
Doris Liu6a83d522013-07-02 12:03:32 -0700415 public void enableRecordingLocation(boolean enable) {
416 setLocationPreference(enable ? RecordLocationPreference.VALUE_ON
417 : RecordLocationPreference.VALUE_OFF);
Michael Kolb8872c232013-01-29 10:33:22 -0800418 }
419
Angus Kongdcccc512013-08-08 17:06:03 -0700420 @Override
421 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800422 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700423 }
424
425 @Override
426 public void onPreviewUIDestroyed() {
427 if (mCameraDevice == null) {
428 return;
429 }
430 mCameraDevice.setPreviewTexture(null);
431 stopPreview();
432 }
433
Michael Kolb8872c232013-01-29 10:33:22 -0800434 private void setLocationPreference(String value) {
435 mPreferences.edit()
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700436 .putString(CameraSettings.KEY_RECORD_LOCATION, value)
437 .apply();
Michael Kolb8872c232013-01-29 10:33:22 -0800438 // TODO: Fix this to use the actual onSharedPreferencesChanged listener
439 // instead of invoking manually
440 onSharedPreferenceChanged();
441 }
442
Michael Kolbd6954f32013-03-08 20:43:01 -0800443 private void onCameraOpened() {
444 View root = mUI.getRootView();
Michael Kolb8872c232013-01-29 10:33:22 -0800445 // These depend on camera parameters.
Michael Kolbd6954f32013-03-08 20:43:01 -0800446
447 int width = root.getWidth();
448 int height = root.getHeight();
Doris Liu6a0de792013-02-26 10:54:25 -0800449 mFocusManager.setPreviewSize(width, height);
Michael Kolbd6954f32013-03-08 20:43:01 -0800450 openCameraCommon();
Michael Kolb8872c232013-01-29 10:33:22 -0800451 }
452
Michael Kolbd6954f32013-03-08 20:43:01 -0800453 private void switchCamera() {
454 if (mPaused) return;
455
456 Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
457 mCameraId = mPendingSwitchCameraId;
458 mPendingSwitchCameraId = -1;
459 setCameraId(mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800460 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800461
Michael Kolbd6954f32013-03-08 20:43:01 -0800462 mUI.collapseCameraControls();
463 mUI.clearFaces();
464 if (mFocusManager != null) mFocusManager.removeMessages();
465
466 // Restart the camera and initialize the UI. From onCreate.
467 mPreferences.setLocalId(mActivity, mCameraId);
468 CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
Angus Kong20fad242013-11-11 18:23:46 -0800469 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
Doris Liu29da2db2013-08-28 14:28:45 -0700470 mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
471 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700472 // Start switch camera animation. Post a message because
473 // onFrameAvailable from the old camera may already exist.
474 mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION);
Doris Liu48239f42013-03-04 22:19:10 -0800475 }
476
Michael Kolbd6954f32013-03-08 20:43:01 -0800477 protected void setCameraId(int cameraId) {
478 ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_ID);
479 pref.setValue("" + cameraId);
480 }
481
482 // either open a new camera or switch cameras
483 private void openCameraCommon() {
Michael Kolb8872c232013-01-29 10:33:22 -0800484 loadCameraPreferences();
Michael Kolbd6954f32013-03-08 20:43:01 -0800485
486 mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this);
Angus Kong0fb819b2013-10-08 13:44:19 -0700487 if (mIsImageCaptureIntent) {
488 mUI.overrideSettings(CameraSettings.KEY_CAMERA_HDR_PLUS,
489 mActivity.getString(R.string.setting_off_value));
490 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800491 updateSceneMode();
492 showTapToFocusToastIfNeeded();
Michael Kolb8872c232013-01-29 10:33:22 -0800493 }
494
ztenghui7b265a62013-09-09 14:58:44 -0700495 @Override
Doris Liu36ebcb12013-10-28 14:44:24 -0700496 public void onPreviewRectChanged(Rect previewRect) {
497 if (mFocusManager != null) mFocusManager.setPreviewRect(previewRect);
Michael Kolbd6954f32013-03-08 20:43:01 -0800498 }
Michael Kolb8872c232013-01-29 10:33:22 -0800499
500 private void resetExposureCompensation() {
501 String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
502 CameraSettings.EXPOSURE_DEFAULT_VALUE);
503 if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
504 Editor editor = mPreferences.edit();
505 editor.putString(CameraSettings.KEY_EXPOSURE, "0");
506 editor.apply();
507 }
508 }
509
510 private void keepMediaProviderInstance() {
511 // We want to keep a reference to MediaProvider in camera's lifecycle.
512 // TODO: Utilize mMediaProviderClient instance to replace
513 // ContentResolver calls.
514 if (mMediaProviderClient == null) {
515 mMediaProviderClient = mContentResolver
516 .acquireContentProviderClient(MediaStore.AUTHORITY);
517 }
518 }
519
520 // Snapshots can only be taken after this is called. It should be called
521 // once only. We could have done these things in onCreate() but we want to
522 // make preview screen appear as soon as possible.
523 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700524 if (mFirstTimeInitialized || mPaused) {
525 return;
526 }
Michael Kolb8872c232013-01-29 10:33:22 -0800527
528 // Initialize location service.
529 boolean recordLocation = RecordLocationPreference.get(
530 mPreferences, mContentResolver);
531 mLocationManager.recordLocation(recordLocation);
532
533 keepMediaProviderInstance();
534
Michael Kolbd6954f32013-03-08 20:43:01 -0800535 mUI.initializeFirstTime();
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800536 MediaSaver s = mActivity.getMediaSaver();
Angus Kong86d36312013-01-31 18:22:44 -0800537 // We set the listener only when both service and shutterbutton
538 // are initialized.
539 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800540 s.setQueueListener(this);
Angus Kong86d36312013-01-31 18:22:44 -0800541 }
Michael Kolb8872c232013-01-29 10:33:22 -0800542
Michael Kolb8872c232013-01-29 10:33:22 -0800543 mNamedImages = new NamedImages();
544
545 mFirstTimeInitialized = true;
546 addIdleHandler();
547
548 mActivity.updateStorageSpaceAndHint();
549 }
550
Michael Kolbd6954f32013-03-08 20:43:01 -0800551 // If the activity is paused and resumed, this method will be called in
552 // onResume.
553 private void initializeSecondTime() {
554 // Start location update if needed.
555 boolean recordLocation = RecordLocationPreference.get(
556 mPreferences, mContentResolver);
557 mLocationManager.recordLocation(recordLocation);
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800558 MediaSaver s = mActivity.getMediaSaver();
Michael Kolbd6954f32013-03-08 20:43:01 -0800559 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800560 s.setQueueListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800561 }
562 mNamedImages = new NamedImages();
563 mUI.initializeSecondTime(mParameters);
564 keepMediaProviderInstance();
565 }
566
Michael Kolb8872c232013-01-29 10:33:22 -0800567 private void showTapToFocusToastIfNeeded() {
568 // Show the tap to focus toast if this is the first start.
569 if (mFocusAreaSupported &&
570 mPreferences.getBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, true)) {
571 // Delay the toast for one second to wait for orientation.
572 mHandler.sendEmptyMessageDelayed(SHOW_TAP_TO_FOCUS_TOAST, 1000);
573 }
574 }
575
576 private void addIdleHandler() {
577 MessageQueue queue = Looper.myQueue();
578 queue.addIdleHandler(new MessageQueue.IdleHandler() {
579 @Override
580 public boolean queueIdle() {
581 Storage.ensureOSXCompatible();
582 return false;
583 }
584 });
585 }
586
Michael Kolb8872c232013-01-29 10:33:22 -0800587 @Override
588 public void startFaceDetection() {
Michael Kolb8872c232013-01-29 10:33:22 -0800589 if (mFaceDetectionStarted) return;
590 if (mParameters.getMaxNumDetectedFaces() > 0) {
591 mFaceDetectionStarted = true;
Michael Kolb8872c232013-01-29 10:33:22 -0800592 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Michael Kolbd6954f32013-03-08 20:43:01 -0800593 mUI.onStartFaceDetection(mDisplayOrientation,
594 (info.facing == CameraInfo.CAMERA_FACING_FRONT));
Angus Kong9e765522013-07-31 14:05:20 -0700595 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800596 mCameraDevice.startFaceDetection();
597 }
598 }
599
Michael Kolb8872c232013-01-29 10:33:22 -0800600 @Override
601 public void stopFaceDetection() {
Michael Kolb8872c232013-01-29 10:33:22 -0800602 if (!mFaceDetectionStarted) return;
603 if (mParameters.getMaxNumDetectedFaces() > 0) {
604 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700605 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800606 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800607 mUI.clearFaces();
Michael Kolb8872c232013-01-29 10:33:22 -0800608 }
609 }
610
Michael Kolb8872c232013-01-29 10:33:22 -0800611 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700612 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700613
Sascha Haeberling37f36112013-08-06 14:31:52 -0700614 private boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700615
Sascha Haeberling37f36112013-08-06 14:31:52 -0700616 public ShutterCallback(boolean needsAnimation) {
617 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700618 }
619
Michael Kolb8872c232013-01-29 10:33:22 -0800620 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700621 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800622 mShutterCallbackTime = System.currentTimeMillis();
623 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
624 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700625 if (mNeedsAnimation) {
626 mActivity.runOnUiThread(new Runnable() {
627 @Override
628 public void run() {
629 animateAfterShutter();
630 }
631 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700632 }
Michael Kolb8872c232013-01-29 10:33:22 -0800633 }
634 }
635
Angus Kong9ef99252013-07-18 18:04:19 -0700636 private final class PostViewPictureCallback
637 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800638 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700639 public void onPictureTaken(byte [] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800640 mPostViewPictureCallbackTime = System.currentTimeMillis();
641 Log.v(TAG, "mShutterToPostViewCallbackTime = "
642 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
643 + "ms");
644 }
645 }
646
Angus Kong9ef99252013-07-18 18:04:19 -0700647 private final class RawPictureCallback
648 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800649 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700650 public void onPictureTaken(byte [] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800651 mRawPictureCallbackTime = System.currentTimeMillis();
652 Log.v(TAG, "mShutterToRawCallbackTime = "
653 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
654 }
655 }
656
Angus Kong9ef99252013-07-18 18:04:19 -0700657 private final class JpegPictureCallback
658 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800659 Location mLocation;
660
661 public JpegPictureCallback(Location loc) {
662 mLocation = loc;
663 }
664
665 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700666 public void onPictureTaken(final byte [] jpegData, CameraProxy camera) {
Sascha Haeberling88901942013-08-28 17:49:00 -0700667 mUI.enableShutter(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800668 if (mPaused) {
669 return;
670 }
Doris Liu6432cd62013-06-13 17:20:31 -0700671 if (mIsImageCaptureIntent) {
672 stopPreview();
673 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700674 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700675 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800676 }
677
678 mJpegPictureCallbackTime = System.currentTimeMillis();
679 // If postview callback has arrived, the captured image is displayed
680 // in postview callback. If not, the captured image is displayed in
681 // raw picture callback.
682 if (mPostViewPictureCallbackTime != 0) {
683 mShutterToPictureDisplayedTime =
684 mPostViewPictureCallbackTime - mShutterCallbackTime;
685 mPictureDisplayedToJpegCallbackTime =
686 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
687 } else {
688 mShutterToPictureDisplayedTime =
689 mRawPictureCallbackTime - mShutterCallbackTime;
690 mPictureDisplayedToJpegCallbackTime =
691 mJpegPictureCallbackTime - mRawPictureCallbackTime;
692 }
693 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
694 + mPictureDisplayedToJpegCallbackTime + "ms");
695
Michael Kolb8872c232013-01-29 10:33:22 -0800696 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
697 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700698 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800699 }
700
Doris Liu36e56fb2013-09-11 17:38:08 -0700701 ExifInterface exif = Exif.getExif(jpegData);
702 int orientation = Exif.getOrientation(exif);
Ruben Brunkd217ed02013-10-08 23:31:13 -0700703
Ruben Brunkd7488272013-10-10 18:45:53 -0700704 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -0800705 // Calculate the width and the height of the jpeg.
706 Size s = mParameters.getPictureSize();
Michael Kolb8872c232013-01-29 10:33:22 -0800707 int width, height;
708 if ((mJpegRotation + orientation) % 180 == 0) {
709 width = s.width;
710 height = s.height;
711 } else {
712 width = s.height;
713 height = s.width;
714 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700715 NamedEntity name = mNamedImages.getNextNameEntity();
716 String title = (name == null) ? null : name.title;
717 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -0700718
719 // Handle debug mode outputs
720 if (mDebugUri != null) {
721 // If using a debug uri, save jpeg there.
722 saveToDebugUri(jpegData);
723
724 // Adjust the title of the debug image shown in mediastore.
725 if (title != null) {
726 title = DEBUG_IMAGE_PREFIX + title;
727 }
728 }
729
Michael Kolb8872c232013-01-29 10:33:22 -0800730 if (title == null) {
731 Log.e(TAG, "Unbalanced name/data pair");
732 } else {
733 if (date == -1) date = mCaptureStartTime;
Angus Kong0d00a892013-03-26 11:40:40 -0700734 if (mHeading >= 0) {
735 // heading direction has been updated by the sensor.
736 ExifTag directionRefTag = exif.buildTag(
737 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
738 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
739 ExifTag directionTag = exif.buildTag(
740 ExifInterface.TAG_GPS_IMG_DIRECTION,
741 new Rational(mHeading, 1));
742 exif.setTag(directionRefTag);
743 exif.setTag(directionTag);
744 }
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800745 mActivity.getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -0800746 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -0700747 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -0800748 }
Doris Liuce2acbc2013-08-21 18:45:29 -0700749 // Animate capture with real jpeg data instead of a preview frame.
Doris Liu29da2db2013-08-28 14:28:45 -0700750 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800751 } else {
752 mJpegImageData = jpegData;
753 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -0700754 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -0800755 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -0800756 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -0800757 }
758 }
759
760 // Check this in advance of each shot so we don't add to shutter
761 // latency. It's true that someone else could write to the SD card in
762 // the mean time and fill it, but that could have happened between the
763 // shutter press and saving the JPEG too.
764 mActivity.updateStorageSpaceAndHint();
765
766 long now = System.currentTimeMillis();
767 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
768 Log.v(TAG, "mJpegCallbackFinishTime = "
769 + mJpegCallbackFinishTime + "ms");
770 mJpegPictureCallbackTime = 0;
771 }
772 }
773
Angus Kong9ef99252013-07-18 18:04:19 -0700774 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800775 @Override
776 public void onAutoFocus(
Angus Kong9ef99252013-07-18 18:04:19 -0700777 boolean focused, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800778 if (mPaused) return;
779
780 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
781 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms");
782 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800783 mFocusManager.onAutoFocus(focused, mUI.isShutterPressed());
Michael Kolb8872c232013-01-29 10:33:22 -0800784 }
785 }
786
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700787 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -0800788 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700789 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800790 @Override
791 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700792 boolean moving, CameraProxy camera) {
793 mFocusManager.onAutoFocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -0800794 }
795 }
796
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700797 /**
798 * This class is just a thread-safe queue for name,date holder objects.
799 */
800 public static class NamedImages {
801 private Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -0800802
803 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700804 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -0800805 }
806
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700807 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -0800808 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700809 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -0800810 r.date = date;
811 mQueue.add(r);
812 }
813
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700814 public NamedEntity getNextNameEntity() {
815 synchronized(mQueue) {
816 if (!mQueue.isEmpty()) {
817 return mQueue.remove(0);
818 }
Michael Kolb8872c232013-01-29 10:33:22 -0800819 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700820 return null;
Michael Kolb8872c232013-01-29 10:33:22 -0800821 }
822
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700823 public static class NamedEntity {
824 public String title;
825 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -0800826 }
827 }
828
829 private void setCameraState(int state) {
830 mCameraState = state;
831 switch (state) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700832 case PhotoController.PREVIEW_STOPPED:
833 case PhotoController.SNAPSHOT_IN_PROGRESS:
834 case PhotoController.SWITCHING_CAMERA:
835 mUI.enableGestures(false);
836 break;
837 case PhotoController.IDLE:
838 mUI.enableGestures(true);
839 break;
Michael Kolb8872c232013-01-29 10:33:22 -0800840 }
841 }
842
Sascha Haeberling37f36112013-08-06 14:31:52 -0700843 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800844 // Only animate when in full screen capture mode
845 // i.e. If monkey/a user swipes to the gallery during picture taking,
846 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -0700847 if (!mIsImageCaptureIntent) {
848 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -0700849 }
Michael Kolb8872c232013-01-29 10:33:22 -0800850 }
851
852 @Override
853 public boolean capture() {
854 // If we are already in the middle of taking a snapshot or the image save request
855 // is full then ignore.
856 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Angus Kong86d36312013-01-31 18:22:44 -0800857 || mCameraState == SWITCHING_CAMERA
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800858 || mActivity.getMediaSaver() == null
859 || mActivity.getMediaSaver().isQueueFull()) {
Michael Kolb8872c232013-01-29 10:33:22 -0800860 return false;
861 }
862 mCaptureStartTime = System.currentTimeMillis();
863 mPostViewPictureCallbackTime = 0;
864 mJpegImageData = null;
865
Angus Kongb50b5cb2013-08-09 14:55:20 -0700866 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -0800867
868 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700869 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -0800870 }
871
872 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -0800873 int orientation;
874 // We need to be consistent with the framework orientation (i.e. the
875 // orientation of the UI.) when the auto-rotate screen setting is on.
876 if (mActivity.isAutoRotateScreen()) {
877 orientation = (360 - mDisplayRotation) % 360;
878 } else {
879 orientation = mOrientation;
880 }
Angus Kong20fad242013-11-11 18:23:46 -0800881 mJpegRotation = CameraUtil.getJpegRotation(mActivity, mCameraId, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800882 mParameters.setRotation(mJpegRotation);
883 Location loc = mLocationManager.getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -0700884 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -0800885 mCameraDevice.setParameters(mParameters);
886
Sascha Haeberling88901942013-08-28 17:49:00 -0700887 // We don't want user to press the button again while taking a
888 // multi-second HDR photo.
889 mUI.enableShutter(false);
Angus Kong9ef99252013-07-18 18:04:19 -0700890 mCameraDevice.takePicture(mHandler,
891 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -0700892 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -0700893 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -0800894
Ruben Brunka9d66bd2013-09-06 11:56:32 -0700895 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -0800896
897 mFaceDetectionStarted = false;
898 setCameraState(SNAPSHOT_IN_PROGRESS);
Bobby Georgescu301b6462013-04-01 15:33:17 -0700899 UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA,
Seth Raphaelcbd82672013-11-05 10:12:36 -0800900 UsageStatistics.ACTION_CAPTURE_DONE, "Photo", 0,
901 UsageStatistics.hashFileName(mNamedImages.mQueue.lastElement().title + ".jpg"));
Michael Kolb8872c232013-01-29 10:33:22 -0800902 return true;
903 }
904
905 @Override
906 public void setFocusParameters() {
907 setCameraParameters(UPDATE_PARAM_PREFERENCE);
908 }
909
910 private int getPreferredCameraId(ComboPreferences preferences) {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700911 int intentCameraId = CameraUtil.getCameraFacingIntentExtras(mActivity);
Michael Kolb8872c232013-01-29 10:33:22 -0800912 if (intentCameraId != -1) {
913 // Testing purpose. Launch a specific camera through the intent
914 // extras.
915 return intentCameraId;
916 } else {
917 return CameraSettings.readPreferredCameraId(preferences);
918 }
919 }
920
Michael Kolbd6954f32013-03-08 20:43:01 -0800921 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -0800922 // If scene mode is set, we cannot set flash mode, white balance, and
923 // focus mode, instead, we read it from driver
924 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
925 overrideCameraSettings(mParameters.getFlashMode(),
926 mParameters.getWhiteBalance(), mParameters.getFocusMode());
927 } else {
928 overrideCameraSettings(null, null, null);
929 }
930 }
931
932 private void overrideCameraSettings(final String flashMode,
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700933 final String whiteBalance, final String focusMode) {
Michael Kolbd6954f32013-03-08 20:43:01 -0800934 mUI.overrideSettings(
935 CameraSettings.KEY_FLASH_MODE, flashMode,
936 CameraSettings.KEY_WHITE_BALANCE, whiteBalance,
937 CameraSettings.KEY_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -0800938 }
939
940 private void loadCameraPreferences() {
941 CameraSettings settings = new CameraSettings(mActivity, mInitialParams,
942 mCameraId, CameraHolder.instance().getCameraInfo());
943 mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences);
944 }
945
946 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800947 public void onOrientationChanged(int orientation) {
948 // We keep the last known orientation. So if the user first orient
949 // the camera then point the camera to floor or sky, we still have
950 // the correct orientation.
951 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
Angus Kongb50b5cb2013-08-09 14:55:20 -0700952 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800953
954 // Show the toast after getting the first orientation changed.
955 if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) {
956 mHandler.removeMessages(SHOW_TAP_TO_FOCUS_TOAST);
957 showTapToFocusToast();
958 }
959 }
960
961 @Override
Angus Kong20fad242013-11-11 18:23:46 -0800962 public void onCameraAvailable(CameraProxy cameraProxy) {
963 if (mPaused) {
964 return;
965 }
966 mCameraDevice = cameraProxy;
967
968 initializeCapabilities();
969
970 // Reset zoom value index.
971 mZoomValue = 0;
972 if (mFocusManager == null) {
973 initializeFocusManager();
974 }
975 mFocusManager.setParameters(mInitialParams);
976
977 mParameters = mCameraDevice.getParameters();
978 setCameraParameters(UPDATE_PARAM_ALL);
979 mCameraPreviewParamsReady = true;
980 startPreview();
981
982 onCameraOpened();
983 }
984
985 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800986 public void onStop() {
987 if (mMediaProviderClient != null) {
988 mMediaProviderClient.release();
989 mMediaProviderClient = null;
990 }
991 }
992
Michael Kolbd6954f32013-03-08 20:43:01 -0800993 @Override
994 public void onCaptureCancelled() {
995 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
996 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -0800997 }
998
Michael Kolbd6954f32013-03-08 20:43:01 -0800999 @Override
1000 public void onCaptureRetake() {
Michael Kolb8872c232013-01-29 10:33:22 -08001001 if (mPaused)
1002 return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001003 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001004 setupPreview();
1005 }
1006
Michael Kolbd6954f32013-03-08 20:43:01 -08001007 @Override
1008 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001009 if (mPaused) {
1010 return;
1011 }
1012
1013 byte[] data = mJpegImageData;
1014
1015 if (mCropValue == null) {
1016 // First handle the no crop case -- just return the value. If the
1017 // caller specifies a "save uri" then write the data to its
1018 // stream. Otherwise, pass back a scaled down version of the bitmap
1019 // directly in the extras.
1020 if (mSaveUri != null) {
1021 OutputStream outputStream = null;
1022 try {
1023 outputStream = mContentResolver.openOutputStream(mSaveUri);
1024 outputStream.write(data);
1025 outputStream.close();
1026
1027 mActivity.setResultEx(Activity.RESULT_OK);
1028 mActivity.finish();
1029 } catch (IOException ex) {
1030 // ignore exception
1031 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001032 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001033 }
1034 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001035 ExifInterface exif = Exif.getExif(data);
1036 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001037 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1038 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001039 mActivity.setResultEx(Activity.RESULT_OK,
1040 new Intent("inline-data").putExtra("data", bitmap));
1041 mActivity.finish();
1042 }
1043 } else {
1044 // Save the image to a temp file and invoke the cropper
1045 Uri tempUri = null;
1046 FileOutputStream tempStream = null;
1047 try {
1048 File path = mActivity.getFileStreamPath(sTempCropFilename);
1049 path.delete();
1050 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1051 tempStream.write(data);
1052 tempStream.close();
1053 tempUri = Uri.fromFile(path);
1054 } catch (FileNotFoundException ex) {
1055 mActivity.setResultEx(Activity.RESULT_CANCELED);
1056 mActivity.finish();
1057 return;
1058 } catch (IOException ex) {
1059 mActivity.setResultEx(Activity.RESULT_CANCELED);
1060 mActivity.finish();
1061 return;
1062 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001063 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001064 }
1065
1066 Bundle newExtras = new Bundle();
1067 if (mCropValue.equals("circle")) {
1068 newExtras.putString("circleCrop", "true");
1069 }
1070 if (mSaveUri != null) {
1071 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1072 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001073 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001074 }
1075 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001076 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001077 }
1078
Sascha Haeberling37f36112013-08-06 14:31:52 -07001079 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001080 final String CROP_ACTION = "com.android.camera.action.CROP";
1081 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001082
1083 cropIntent.setData(tempUri);
1084 cropIntent.putExtras(newExtras);
1085
1086 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1087 }
1088 }
1089
Michael Kolb8872c232013-01-29 10:33:22 -08001090 @Override
1091 public void onShutterButtonFocus(boolean pressed) {
Michael Kolbd6954f32013-03-08 20:43:01 -08001092 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001093 || (mCameraState == SNAPSHOT_IN_PROGRESS)
1094 || (mCameraState == PREVIEW_STOPPED)) return;
1095
1096 // Do not do focus if there is not enough storage.
1097 if (pressed && !canTakePicture()) return;
1098
1099 if (pressed) {
Michael Kolb8872c232013-01-29 10:33:22 -08001100 mFocusManager.onShutterDown();
1101 } else {
Doris Liuda50e052013-02-07 14:36:32 -08001102 // for countdown mode, we need to postpone the shutter release
1103 // i.e. lock the focus during countdown.
Michael Kolbd6954f32013-03-08 20:43:01 -08001104 if (!mUI.isCountingDown()) {
Doris Liuda50e052013-02-07 14:36:32 -08001105 mFocusManager.onShutterUp();
1106 }
Michael Kolb8872c232013-01-29 10:33:22 -08001107 }
1108 }
1109
1110 @Override
1111 public void onShutterButtonClick() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001112 if (mPaused || mUI.collapseCameraControls()
Michael Kolb8872c232013-01-29 10:33:22 -08001113 || (mCameraState == SWITCHING_CAMERA)
1114 || (mCameraState == PREVIEW_STOPPED)) return;
1115
1116 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001117 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001118 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001119 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001120 return;
1121 }
1122 Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
1123
Angus Kongb50b5cb2013-08-09 14:55:20 -07001124 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001125 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001126 }
Michael Kolb8872c232013-01-29 10:33:22 -08001127 // If the user wants to do a snapshot while the previous one is still
1128 // in progress, remember the fact and do it after we finish the previous
1129 // one and re-start the preview. Snapshot in progress also includes the
1130 // state that autofocus is focusing and a picture will be taken when
1131 // focus callback arrives.
1132 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)
1133 && !mIsImageCaptureIntent) {
1134 mSnapshotOnIdle = true;
1135 return;
1136 }
1137
1138 String timer = mPreferences.getString(
1139 CameraSettings.KEY_TIMER,
1140 mActivity.getString(R.string.pref_camera_timer_default));
1141 boolean playSound = mPreferences.getString(CameraSettings.KEY_TIMER_SOUND_EFFECTS,
1142 mActivity.getString(R.string.pref_camera_timer_sound_default))
1143 .equals(mActivity.getString(R.string.setting_on_value));
1144
1145 int seconds = Integer.parseInt(timer);
1146 // When shutter button is pressed, check whether the previous countdown is
1147 // finished. If not, cancel the previous countdown and start a new one.
Michael Kolbd6954f32013-03-08 20:43:01 -08001148 if (mUI.isCountingDown()) {
1149 mUI.cancelCountDown();
1150 }
1151 if (seconds > 0) {
1152 mUI.startCountDown(seconds, playSound);
Michael Kolb8872c232013-01-29 10:33:22 -08001153 } else {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001154 mSnapshotOnIdle = false;
1155 mFocusManager.doSnap();
Michael Kolb8872c232013-01-29 10:33:22 -08001156 }
1157 }
1158
1159 @Override
1160 public void installIntentFilter() {
Sascha Haeberling4de78802013-10-06 18:07:07 -07001161 // Do nothing.
Michael Kolb8872c232013-01-29 10:33:22 -08001162 }
1163
1164 @Override
1165 public boolean updateStorageHintOnResume() {
1166 return mFirstTimeInitialized;
1167 }
1168
1169 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001170 public void onResumeBeforeSuper() {
1171 mPaused = false;
1172 }
1173
1174 @Override
1175 public void onResumeAfterSuper() {
Doris Liu3a45c332013-10-15 19:10:28 -07001176 Log.v(TAG, "On resume.");
Michael Kolb8872c232013-01-29 10:33:22 -08001177 if (mOpenCameraFail || mCameraDisabled) return;
1178
Angus Kong20fad242013-11-11 18:23:46 -08001179 mActivity.getCameraProvider().requestCamera(mCameraId);
1180
Michael Kolb8872c232013-01-29 10:33:22 -08001181 mJpegPictureCallbackTime = 0;
1182 mZoomValue = 0;
Angus Kongdcccc512013-08-08 17:06:03 -07001183 resetExposureCompensation();
Angus Kong20fad242013-11-11 18:23:46 -08001184
1185 mOnResumeTime = SystemClock.uptimeMillis();
1186 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001187
1188 // If first time initialization is not finished, put it in the
1189 // message queue.
1190 if (!mFirstTimeInitialized) {
1191 mHandler.sendEmptyMessage(FIRST_TIME_INIT);
1192 } else {
1193 initializeSecondTime();
1194 }
Doris Liu59390062013-10-10 17:20:56 -07001195 mUI.initDisplayChangeListener();
Michael Kolb8872c232013-01-29 10:33:22 -08001196 keepScreenOnAwhile();
1197
Bobby Georgescu0a7dd572013-03-12 22:45:17 -07001198 UsageStatistics.onContentViewChanged(
1199 UsageStatistics.COMPONENT_CAMERA, "PhotoModule");
Angus Kong0d00a892013-03-26 11:40:40 -07001200
1201 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1202 if (gsensor != null) {
1203 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1204 }
1205
1206 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1207 if (msensor != null) {
1208 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1209 }
Michael Kolb8872c232013-01-29 10:33:22 -08001210 }
1211
Michael Kolb8872c232013-01-29 10:33:22 -08001212 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001213 public void onPauseAfterSuper() {
1214 }
1215
1216 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001217 public void onPauseBeforeSuper() {
Angus Kong20fad242013-11-11 18:23:46 -08001218 Log.v(TAG, "On pause.");
Michael Kolb8872c232013-01-29 10:33:22 -08001219 mPaused = true;
Angus Kong0d00a892013-03-26 11:40:40 -07001220 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1221 if (gsensor != null) {
1222 mSensorManager.unregisterListener(this, gsensor);
1223 }
1224
1225 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1226 if (msensor != null) {
1227 mSensorManager.unregisterListener(this, msensor);
1228 }
Doris Liu3a45c332013-10-15 19:10:28 -07001229 mUI.showPreviewCover();
Michael Kolb8872c232013-01-29 10:33:22 -08001230 // When camera is started from secure lock screen for the first time
1231 // after screen on, the activity gets onCreate->onResume->onPause->onResume.
1232 // To reduce the latency, keep the camera for a short time so it does
1233 // not need to be opened again.
1234 if (mCameraDevice != null && mActivity.isSecureCamera()
Angus Kong6a8e8a12013-07-19 14:55:07 -07001235 && CameraActivity.isFirstStartAfterScreenOn()) {
1236 CameraActivity.resetFirstStartAfterScreenOn();
Michael Kolb8872c232013-01-29 10:33:22 -08001237 CameraHolder.instance().keep(KEEP_CAMERA_TIMEOUT);
1238 }
1239 // Reset the focus first. Camera CTS does not guarantee that
1240 // cancelAutoFocus is allowed after preview stops.
1241 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1242 mCameraDevice.cancelAutoFocus();
1243 }
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001244 // If the camera has not been opened asynchronously yet,
1245 // and startPreview hasn't been called, then this is a no-op.
1246 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001247 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001248
Angus Kongce5480e2013-01-29 17:43:48 -08001249 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001250
1251 if (mLocationManager != null) mLocationManager.recordLocation(false);
1252
1253 // If we are in an image capture intent and has taken
1254 // a picture, we just clear it in onPause.
1255 mJpegImageData = null;
1256
Angus Kongdcccc512013-08-08 17:06:03 -07001257 // Remove the messages and runnables in the queue.
1258 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001259
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001260 // Postpones actually releasing for KEEP_CAMERA_TIMEOUT,
1261 // so if onResume is directly called after this, the camera
1262 // simply needs to reconnect (takes about 2-5ms).
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001263 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -08001264
1265 resetScreenOn();
1266 mUI.onPause();
1267
Michael Kolb8872c232013-01-29 10:33:22 -08001268 mPendingSwitchCameraId = -1;
1269 if (mFocusManager != null) mFocusManager.removeMessages();
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001270 MediaSaver s = mActivity.getMediaSaver();
Angus Kong86d36312013-01-31 18:22:44 -08001271 if (s != null) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001272 s.setQueueListener(null);
Angus Kong86d36312013-01-31 18:22:44 -08001273 }
Doris Liu59390062013-10-10 17:20:56 -07001274 mUI.removeDisplayChangeListener();
Michael Kolb8872c232013-01-29 10:33:22 -08001275 }
1276
Michael Kolb8872c232013-01-29 10:33:22 -08001277 /**
1278 * The focus manager is the first UI related element to get initialized,
1279 * and it requires the RenderOverlay, so initialize it here
1280 */
1281 private void initializeFocusManager() {
1282 // Create FocusManager object. startPreview needs it.
Michael Kolb8872c232013-01-29 10:33:22 -08001283 // if mFocusManager not null, reuse it
1284 // otherwise create a new instance
1285 if (mFocusManager != null) {
1286 mFocusManager.removeMessages();
1287 } else {
1288 CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
Doris Liu29da2db2013-08-28 14:28:45 -07001289 mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
Michael Kolb8872c232013-01-29 10:33:22 -08001290 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1291 R.array.pref_camera_focusmode_default_array);
1292 mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes,
Doris Liu29da2db2013-08-28 14:28:45 -07001293 mInitialParams, this, mMirror,
Michael Kolbd6954f32013-03-08 20:43:01 -08001294 mActivity.getMainLooper(), mUI);
Michael Kolb8872c232013-01-29 10:33:22 -08001295 }
1296 }
1297
Michael Kolb8872c232013-01-29 10:33:22 -08001298 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001299 public void init(AppController app, boolean isSecureCamera, boolean isCaptureIntent) {
1300 init((CameraActivity) app.getAndroidContext(), app.getModuleLayoutRoot());
1301 }
1302
1303 @Override
1304 public void resume() {
1305 onResumeBeforeSuper();
1306 onResumeAfterSuper();
1307 }
1308
1309 @Override
1310 public void pause() {
1311 onPauseBeforeSuper();
1312 onPauseAfterSuper();
1313 }
1314
1315 @Override
1316 public void destroy() {
1317 // TODO: implement this.
1318 }
1319
1320 @Override
1321 public void onPreviewSizeChanged(int width, int height) {
1322 // TODO: implement this.
1323 }
1324
1325 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001326 public void onConfigurationChanged(Configuration newConfig) {
1327 Log.v(TAG, "onConfigurationChanged");
1328 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001329 }
1330
1331 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001332 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001333 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001334 setDisplayOrientation();
1335 }
1336 }
1337
1338 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001339 public void onActivityResult(
1340 int requestCode, int resultCode, Intent data) {
1341 switch (requestCode) {
1342 case REQUEST_CROP: {
1343 Intent intent = new Intent();
1344 if (data != null) {
1345 Bundle extras = data.getExtras();
1346 if (extras != null) {
1347 intent.putExtras(extras);
1348 }
1349 }
1350 mActivity.setResultEx(resultCode, intent);
1351 mActivity.finish();
1352
1353 File path = mActivity.getFileStreamPath(sTempCropFilename);
1354 path.delete();
1355
1356 break;
1357 }
1358 }
1359 }
1360
1361 private boolean canTakePicture() {
Angus Kong2dcc0a92013-09-25 13:00:08 -07001362 return isCameraIdle() && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001363 }
1364
1365 @Override
1366 public void autoFocus() {
1367 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001368 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001369 setCameraState(FOCUSING);
1370 }
1371
1372 @Override
1373 public void cancelAutoFocus() {
1374 mCameraDevice.cancelAutoFocus();
1375 setCameraState(IDLE);
1376 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1377 }
1378
1379 // Preview area is touched. Handle touch focus.
1380 @Override
1381 public void onSingleTapUp(View view, int x, int y) {
1382 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1383 || mCameraState == SNAPSHOT_IN_PROGRESS
1384 || mCameraState == SWITCHING_CAMERA
1385 || mCameraState == PREVIEW_STOPPED) {
1386 return;
1387 }
1388
Michael Kolb8872c232013-01-29 10:33:22 -08001389 // Check if metering area or focus area is supported.
1390 if (!mFocusAreaSupported && !mMeteringAreaSupported) return;
1391 mFocusManager.onSingleTapUp(x, y);
1392 }
1393
1394 @Override
1395 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001396 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001397 }
1398
1399 @Override
1400 public boolean onKeyDown(int keyCode, KeyEvent event) {
1401 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001402 case KeyEvent.KEYCODE_VOLUME_UP:
1403 case KeyEvent.KEYCODE_VOLUME_DOWN:
1404 case KeyEvent.KEYCODE_FOCUS:
1405 if (/*TODO: mActivity.isInCameraApp() &&*/ mFirstTimeInitialized) {
1406 if (event.getRepeatCount() == 0) {
1407 onShutterButtonFocus(true);
1408 }
1409 return true;
1410 }
1411 return false;
1412 case KeyEvent.KEYCODE_CAMERA:
1413 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1414 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001415 }
1416 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001417 case KeyEvent.KEYCODE_DPAD_CENTER:
1418 // If we get a dpad center event without any focused view, move
1419 // the focus to the shutter button and press it.
1420 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1421 // Start auto-focus immediately to reduce shutter lag. After
1422 // the shutter button gets the focus, onShutterButtonFocus()
1423 // will be called again but it is fine.
1424 onShutterButtonFocus(true);
1425 mUI.pressShutterButton();
1426 }
1427 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001428 }
1429 return false;
1430 }
1431
1432 @Override
1433 public boolean onKeyUp(int keyCode, KeyEvent event) {
1434 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001435 case KeyEvent.KEYCODE_VOLUME_UP:
1436 case KeyEvent.KEYCODE_VOLUME_DOWN:
1437 if (/*mActivity.isInCameraApp() && */ mFirstTimeInitialized) {
1438 onShutterButtonClick();
1439 return true;
1440 }
1441 return false;
1442 case KeyEvent.KEYCODE_FOCUS:
1443 if (mFirstTimeInitialized) {
1444 onShutterButtonFocus(false);
1445 }
Michael Kolb8872c232013-01-29 10:33:22 -08001446 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001447 }
1448 return false;
1449 }
1450
Michael Kolb8872c232013-01-29 10:33:22 -08001451 private void closeCamera() {
1452 if (mCameraDevice != null) {
1453 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001454 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001455 mCameraDevice.setErrorCallback(null);
Ruben Brunk59147832013-11-05 15:53:28 -08001456
Michael Kolb8872c232013-01-29 10:33:22 -08001457 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001458 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001459 mCameraDevice = null;
1460 setCameraState(PREVIEW_STOPPED);
1461 mFocusManager.onCameraReleased();
1462 }
1463 }
1464
1465 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001466 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1467 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001468 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001469 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001470 if (mFocusManager != null) {
1471 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1472 }
Doris Liu6432cd62013-06-13 17:20:31 -07001473 // Change the camera display orientation
1474 if (mCameraDevice != null) {
1475 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1476 }
Michael Kolb8872c232013-01-29 10:33:22 -08001477 }
1478
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001479 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001480 private void setupPreview() {
1481 mFocusManager.resetTouchFocus();
1482 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001483 }
1484
Angus Kong20fad242013-11-11 18:23:46 -08001485 /**
1486 * Returns whether we can/should start the preview or not.
1487 */
1488 private boolean checkPreviewPreconditions() {
1489 if (mPaused) {
1490 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001491 }
Michael Kolb8872c232013-01-29 10:33:22 -08001492
Angus Kong20fad242013-11-11 18:23:46 -08001493 if (mCameraDevice == null) {
1494 Log.w(TAG, "startPreview: camera device not ready yet.");
1495 return false;
1496 }
1497
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001498 SurfaceTexture st = mUI.getSurfaceTexture();
1499 if (st == null) {
1500 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001501 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001502 }
1503
1504 if (!mCameraPreviewParamsReady) {
1505 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001506 return false;
1507 }
1508 return true;
1509 }
1510
1511 /**
1512 * The start/stop preview should only run on the UI thread.
1513 */
1514 private void startPreview() {
1515 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001516 return;
1517 }
Angus Kong20fad242013-11-11 18:23:46 -08001518
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001519 mCameraDevice.setErrorCallback(mErrorCallback);
1520 // ICS camera frameworks has a bug. Face detection state is not cleared 1589
1521 // after taking a picture. Stop the preview to work around it. The bug
1522 // was fixed in JB.
1523 if (mCameraState != PREVIEW_STOPPED) {
1524 stopPreview();
1525 }
1526
1527 setDisplayOrientation();
1528
1529 if (!mSnapshotOnIdle) {
1530 // If the focus mode is continuous autofocus, call cancelAutoFocus to
1531 // resume it because it may have been paused by autoFocus call.
1532 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
1533 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001534 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001535 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1536 }
1537 setCameraParameters(UPDATE_PARAM_ALL);
1538 // Let UI set its expected aspect ratio
Angus Kong20fad242013-11-11 18:23:46 -08001539 mCameraDevice.setPreviewTexture(mUI.getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001540
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001541 Log.v(TAG, "startPreview");
1542 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001543
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001544 mFocusManager.onPreviewStarted();
1545 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -08001546
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001547 if (mSnapshotOnIdle) {
1548 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001549 }
1550 }
1551
Michael Kolbd6954f32013-03-08 20:43:01 -08001552 @Override
1553 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001554 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1555 Log.v(TAG, "stopPreview");
1556 mCameraDevice.stopPreview();
1557 mFaceDetectionStarted = false;
1558 }
1559 setCameraState(PREVIEW_STOPPED);
1560 if (mFocusManager != null) mFocusManager.onPreviewStopped();
1561 }
1562
1563 @SuppressWarnings("deprecation")
1564 private void updateCameraParametersInitialize() {
1565 // Reset preview frame rate to the maximum because it may be lowered by
1566 // video camera application.
ztenghui16a35202013-09-23 11:35:36 -07001567 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mParameters);
1568 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001569 mParameters.setPreviewFpsRange(
1570 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1571 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001572 }
1573
Angus Kongb50b5cb2013-08-09 14:55:20 -07001574 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001575
1576 // Disable video stabilization. Convenience methods not available in API
1577 // level <= 14
1578 String vstabSupported = mParameters.get("video-stabilization-supported");
1579 if ("true".equals(vstabSupported)) {
1580 mParameters.set("video-stabilization", "false");
1581 }
1582 }
1583
1584 private void updateCameraParametersZoom() {
1585 // Set zoom.
1586 if (mParameters.isZoomSupported()) {
1587 mParameters.setZoom(mZoomValue);
1588 }
1589 }
1590
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001591 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001592 private void setAutoExposureLockIfSupported() {
1593 if (mAeLockSupported) {
1594 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1595 }
1596 }
1597
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001598 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001599 private void setAutoWhiteBalanceLockIfSupported() {
1600 if (mAwbLockSupported) {
1601 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1602 }
1603 }
1604
Michael Kolb8872c232013-01-29 10:33:22 -08001605 private void setFocusAreasIfSupported() {
1606 if (mFocusAreaSupported) {
1607 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1608 }
1609 }
1610
Michael Kolb8872c232013-01-29 10:33:22 -08001611 private void setMeteringAreasIfSupported() {
1612 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001613 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1614 }
1615 }
1616
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001617 private boolean updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001618 setAutoExposureLockIfSupported();
1619 setAutoWhiteBalanceLockIfSupported();
1620 setFocusAreasIfSupported();
1621 setMeteringAreasIfSupported();
1622
1623 // Set picture size.
1624 String pictureSize = mPreferences.getString(
1625 CameraSettings.KEY_PICTURE_SIZE, null);
1626 if (pictureSize == null) {
1627 CameraSettings.initialCameraPictureSize(mActivity, mParameters);
1628 } else {
1629 List<Size> supported = mParameters.getSupportedPictureSizes();
1630 CameraSettings.setCameraPictureSize(
1631 pictureSize, supported, mParameters);
1632 }
1633 Size size = mParameters.getPictureSize();
1634
1635 // Set a preview size that is closest to the viewfinder height and has
1636 // the right aspect ratio.
1637 List<Size> sizes = mParameters.getSupportedPreviewSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001638 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Michael Kolb8872c232013-01-29 10:33:22 -08001639 (double) size.width / size.height);
1640 Size original = mParameters.getPreviewSize();
1641 if (!original.equals(optimalSize)) {
1642 mParameters.setPreviewSize(optimalSize.width, optimalSize.height);
Doris Liu6432cd62013-06-13 17:20:31 -07001643
Michael Kolb8872c232013-01-29 10:33:22 -08001644 // Zoom related settings will be changed for different preview
1645 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07001646 if (mHandler.getLooper() == Looper.myLooper()) {
1647 // On UI thread only, not when camera starts up
1648 setupPreview();
1649 } else {
1650 mCameraDevice.setParameters(mParameters);
1651 }
Michael Kolb8872c232013-01-29 10:33:22 -08001652 mParameters = mCameraDevice.getParameters();
1653 }
Doris Liu95405742013-11-05 15:25:26 -08001654
1655 if(optimalSize.width != 0 && optimalSize.height != 0) {
1656 mUI.updatePreviewAspectRatio((float) optimalSize.width
1657 / (float) optimalSize.height);
1658 }
Michael Kolb8872c232013-01-29 10:33:22 -08001659 Log.v(TAG, "Preview size is " + optimalSize.width + "x" + optimalSize.height);
1660
1661 // Since changing scene mode may change supported values, set scene mode
1662 // first. HDR is a scene mode. To promote it in UI, it is stored in a
1663 // separate preference.
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001664 String onValue = mActivity.getString(R.string.setting_on_value);
Michael Kolb8872c232013-01-29 10:33:22 -08001665 String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR,
1666 mActivity.getString(R.string.pref_camera_hdr_default));
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001667 String hdrPlus = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR_PLUS,
1668 mActivity.getString(R.string.pref_camera_hdr_plus_default));
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001669 boolean hdrOn = onValue.equals(hdr);
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001670 boolean hdrPlusOn = onValue.equals(hdrPlus);
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001671
1672 boolean doGcamModeSwitch = false;
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001673 if (hdrPlusOn && GcamHelper.hasGcamCapture()) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001674 // Kick off mode switch to gcam.
1675 doGcamModeSwitch = true;
Michael Kolb8872c232013-01-29 10:33:22 -08001676 } else {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001677 if (hdrOn) {
1678 mSceneMode = CameraUtil.SCENE_MODE_HDR;
1679 } else {
1680 mSceneMode = mPreferences.getString(
1681 CameraSettings.KEY_SCENE_MODE,
1682 mActivity.getString(R.string.pref_camera_scenemode_default));
1683 }
Michael Kolb8872c232013-01-29 10:33:22 -08001684 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001685 if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
Michael Kolb8872c232013-01-29 10:33:22 -08001686 if (!mParameters.getSceneMode().equals(mSceneMode)) {
1687 mParameters.setSceneMode(mSceneMode);
1688
1689 // Setting scene mode will change the settings of flash mode,
1690 // white balance, and focus mode. Here we read back the
1691 // parameters, so we can know those settings.
1692 mCameraDevice.setParameters(mParameters);
1693 mParameters = mCameraDevice.getParameters();
1694 }
1695 } else {
1696 mSceneMode = mParameters.getSceneMode();
1697 if (mSceneMode == null) {
1698 mSceneMode = Parameters.SCENE_MODE_AUTO;
1699 }
1700 }
1701
1702 // Set JPEG quality.
1703 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1704 CameraProfile.QUALITY_HIGH);
1705 mParameters.setJpegQuality(jpegQuality);
1706
1707 // For the following settings, we need to check if the settings are
1708 // still supported by latest driver, if not, ignore the settings.
1709
1710 // Set exposure compensation
1711 int value = CameraSettings.readExposure(mPreferences);
1712 int max = mParameters.getMaxExposureCompensation();
1713 int min = mParameters.getMinExposureCompensation();
1714 if (value >= min && value <= max) {
1715 mParameters.setExposureCompensation(value);
1716 } else {
1717 Log.w(TAG, "invalid exposure range: " + value);
1718 }
1719
1720 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
1721 // Set flash mode.
1722 String flashMode = mPreferences.getString(
1723 CameraSettings.KEY_FLASH_MODE,
1724 mActivity.getString(R.string.pref_camera_flashmode_default));
1725 List<String> supportedFlash = mParameters.getSupportedFlashModes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001726 if (CameraUtil.isSupported(flashMode, supportedFlash)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001727 mParameters.setFlashMode(flashMode);
1728 } else {
1729 flashMode = mParameters.getFlashMode();
1730 if (flashMode == null) {
1731 flashMode = mActivity.getString(
1732 R.string.pref_camera_flashmode_no_flash);
1733 }
1734 }
1735
1736 // Set white balance parameter.
1737 String whiteBalance = mPreferences.getString(
1738 CameraSettings.KEY_WHITE_BALANCE,
1739 mActivity.getString(R.string.pref_camera_whitebalance_default));
Angus Kongb50b5cb2013-08-09 14:55:20 -07001740 if (CameraUtil.isSupported(whiteBalance,
Michael Kolb8872c232013-01-29 10:33:22 -08001741 mParameters.getSupportedWhiteBalance())) {
1742 mParameters.setWhiteBalance(whiteBalance);
1743 } else {
1744 whiteBalance = mParameters.getWhiteBalance();
1745 if (whiteBalance == null) {
1746 whiteBalance = Parameters.WHITE_BALANCE_AUTO;
1747 }
1748 }
1749
1750 // Set focus mode.
1751 mFocusManager.overrideFocusMode(null);
1752 mParameters.setFocusMode(mFocusManager.getFocusMode());
1753 } else {
1754 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
1755 }
1756
Angus Kongdcccc512013-08-08 17:06:03 -07001757 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
Michael Kolb8872c232013-01-29 10:33:22 -08001758 updateAutoFocusMoveCallback();
1759 }
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001760
1761 return doGcamModeSwitch;
Michael Kolb8872c232013-01-29 10:33:22 -08001762 }
1763
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001764 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001765 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001766 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07001767 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07001768 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08001769 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07001770 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08001771 }
1772 }
1773
1774 // We separate the parameters into several subsets, so we can update only
1775 // the subsets actually need updating. The PREFERENCE set needs extra
1776 // locking because the preference can be changed from GLThread as well.
1777 private void setCameraParameters(int updateSet) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001778 boolean doModeSwitch = false;
1779
Michael Kolb8872c232013-01-29 10:33:22 -08001780 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
1781 updateCameraParametersInitialize();
1782 }
1783
1784 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
1785 updateCameraParametersZoom();
1786 }
1787
1788 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001789 doModeSwitch = updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08001790 }
1791
1792 mCameraDevice.setParameters(mParameters);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001793
Sascha Haeberling9bf0fd62013-10-03 12:10:48 -07001794 // Switch to gcam module if HDR+ was selected
Angus Kong0fb819b2013-10-08 13:44:19 -07001795 if (doModeSwitch && !mIsImageCaptureIntent) {
ztenghui367c7c82013-10-16 14:43:26 -07001796 mHandler.sendEmptyMessage(SWITCH_TO_GCAM_MODULE);
Ruben Brunk4601f5d2013-09-24 18:35:22 -07001797 }
Michael Kolb8872c232013-01-29 10:33:22 -08001798 }
1799
1800 // If the Camera is idle, update the parameters immediately, otherwise
1801 // accumulate them in mUpdateSet and update later.
1802 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
1803 mUpdateSet |= additionalUpdateSet;
1804 if (mCameraDevice == null) {
1805 // We will update all the parameters when we open the device, so
1806 // we don't need to do anything now.
1807 mUpdateSet = 0;
1808 return;
1809 } else if (isCameraIdle()) {
1810 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08001811 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08001812 mUpdateSet = 0;
1813 } else {
1814 if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
1815 mHandler.sendEmptyMessageDelayed(
1816 SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
1817 }
1818 }
1819 }
1820
ztenghui7b265a62013-09-09 14:58:44 -07001821 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001822 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08001823 return (mCameraState == IDLE) ||
1824 (mCameraState == PREVIEW_STOPPED) ||
1825 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
1826 && (mCameraState != SWITCHING_CAMERA));
1827 }
1828
ztenghui7b265a62013-09-09 14:58:44 -07001829 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001830 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08001831 String action = mActivity.getIntent().getAction();
1832 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Angus Kong6a8e8a12013-07-19 14:55:07 -07001833 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08001834 }
1835
1836 private void setupCaptureParams() {
1837 Bundle myExtras = mActivity.getIntent().getExtras();
1838 if (myExtras != null) {
1839 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1840 mCropValue = myExtras.getString("crop");
1841 }
1842 }
1843
Michael Kolb8872c232013-01-29 10:33:22 -08001844 @Override
1845 public void onSharedPreferenceChanged() {
1846 // ignore the events after "onPause()"
1847 if (mPaused) return;
1848
1849 boolean recordLocation = RecordLocationPreference.get(
1850 mPreferences, mContentResolver);
1851 mLocationManager.recordLocation(recordLocation);
1852
1853 setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
Michael Kolbeb8adc12013-04-26 11:09:29 -07001854 mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
Michael Kolb8872c232013-01-29 10:33:22 -08001855 }
1856
1857 @Override
1858 public void onCameraPickerClicked(int cameraId) {
1859 if (mPaused || mPendingSwitchCameraId != -1) return;
1860
1861 mPendingSwitchCameraId = cameraId;
Doris Liu6432cd62013-06-13 17:20:31 -07001862
1863 Log.v(TAG, "Start to switch camera. cameraId=" + cameraId);
1864 // We need to keep a preview frame for the animation before
1865 // releasing the camera. This will trigger onPreviewTextureCopied.
1866 //TODO: Need to animate the camera switch
1867 switchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001868 }
1869
Michael Kolb8872c232013-01-29 10:33:22 -08001870 // Preview texture has been copied. Now camera can be released and the
1871 // animation can be started.
1872 @Override
1873 public void onPreviewTextureCopied() {
1874 mHandler.sendEmptyMessage(SWITCH_CAMERA);
1875 }
1876
1877 @Override
1878 public void onCaptureTextureCopied() {
1879 }
1880
1881 @Override
1882 public void onUserInteraction() {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001883 if (!mActivity.isFinishing()) keepScreenOnAwhile();
Michael Kolb8872c232013-01-29 10:33:22 -08001884 }
1885
1886 private void resetScreenOn() {
1887 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1888 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1889 }
1890
1891 private void keepScreenOnAwhile() {
1892 mHandler.removeMessages(CLEAR_SCREEN_DELAY);
1893 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1894 mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
1895 }
1896
Michael Kolb8872c232013-01-29 10:33:22 -08001897 @Override
1898 public void onOverriddenPreferencesClicked() {
1899 if (mPaused) return;
Michael Kolbd6954f32013-03-08 20:43:01 -08001900 mUI.showPreferencesToast();
Michael Kolb8872c232013-01-29 10:33:22 -08001901 }
1902
1903 private void showTapToFocusToast() {
1904 // TODO: Use a toast?
1905 new RotateTextToast(mActivity, R.string.tap_to_focus, 0).show();
1906 // Clear the preference.
1907 Editor editor = mPreferences.edit();
1908 editor.putBoolean(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, false);
1909 editor.apply();
1910 }
1911
1912 private void initializeCapabilities() {
1913 mInitialParams = mCameraDevice.getParameters();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001914 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
1915 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
1916 mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams);
1917 mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams);
Angus Kongdcccc512013-08-08 17:06:03 -07001918 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07001919 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08001920 }
1921
Michael Kolb8872c232013-01-29 10:33:22 -08001922 @Override
1923 public void onCountDownFinished() {
1924 mSnapshotOnIdle = false;
1925 mFocusManager.doSnap();
Doris Liuda50e052013-02-07 14:36:32 -08001926 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -08001927 }
1928
Michael Kolb8872c232013-01-29 10:33:22 -08001929 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001930 public void onShowSwitcherPopup() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001931 mUI.onShowSwitcherPopup();
Michael Kolb8872c232013-01-29 10:33:22 -08001932 }
1933
Angus Kongce5480e2013-01-29 17:43:48 -08001934 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001935 public int onZoomChanged(int index) {
1936 // Not useful to change zoom value when the activity is paused.
1937 if (mPaused) return index;
1938 mZoomValue = index;
1939 if (mParameters == null || mCameraDevice == null) return index;
1940 // Set zoom parameters asynchronously
1941 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07001942 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08001943 Parameters p = mCameraDevice.getParameters();
1944 if (p != null) return p.getZoom();
1945 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08001946 }
1947
1948 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001949 public int getCameraState() {
1950 return mCameraState;
1951 }
1952
1953 @Override
1954 public void onQueueStatus(boolean full) {
1955 mUI.enableShutter(!full);
Angus Kongce5480e2013-01-29 17:43:48 -08001956 }
Angus Kong86d36312013-01-31 18:22:44 -08001957
1958 @Override
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001959 public void onMediaSaverAvailable(MediaSaver s) {
Angus Kong86d36312013-01-31 18:22:44 -08001960 // We set the listener only when both service and shutterbutton
1961 // are initialized.
Michael Kolbd6954f32013-03-08 20:43:01 -08001962 if (mFirstTimeInitialized) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001963 s.setQueueListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -08001964 }
Angus Kong86d36312013-01-31 18:22:44 -08001965 }
Angus Kong0d00a892013-03-26 11:40:40 -07001966
1967 @Override
1968 public void onAccuracyChanged(Sensor sensor, int accuracy) {
1969 }
1970
1971 @Override
1972 public void onSensorChanged(SensorEvent event) {
1973 int type = event.sensor.getType();
1974 float[] data;
1975 if (type == Sensor.TYPE_ACCELEROMETER) {
1976 data = mGData;
1977 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
1978 data = mMData;
1979 } else {
1980 // we should not be here.
1981 return;
1982 }
1983 for (int i = 0; i < 3 ; i++) {
1984 data[i] = event.values[i];
1985 }
1986 float[] orientation = new float[3];
1987 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
1988 SensorManager.getOrientation(mR, orientation);
1989 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
1990 if (mHeading < 0) {
1991 mHeading += 360;
1992 }
Angus Kong0d00a892013-03-26 11:40:40 -07001993 }
Doris Liu6432cd62013-06-13 17:20:31 -07001994
1995 @Override
ztenghui7b265a62013-09-09 14:58:44 -07001996 public void onPreviewFocusChanged(boolean previewFocused) {
1997 mUI.onPreviewFocusChanged(previewFocused);
Doris Liu6432cd62013-06-13 17:20:31 -07001998 }
1999
Erin Dahlgren3044d8c2013-10-10 18:23:45 -07002000 @Override
2001 public boolean arePreviewControlsVisible() {
2002 return mUI.arePreviewControlsVisible();
2003 }
2004
Ruben Brunkd217ed02013-10-08 23:31:13 -07002005 // For debugging only.
2006 public void setDebugUri(Uri uri) {
2007 mDebugUri = uri;
2008 }
2009
2010 // For debugging only.
2011 private void saveToDebugUri(byte[] data) {
2012 if (mDebugUri != null) {
2013 OutputStream outputStream = null;
2014 try {
2015 outputStream = mContentResolver.openOutputStream(mDebugUri);
2016 outputStream.write(data);
2017 outputStream.close();
2018 } catch (IOException e) {
2019 Log.e(TAG, "Exception while writing debug jpeg file", e);
2020 } finally {
2021 CameraUtil.closeSilently(outputStream);
2022 }
2023 }
2024 }
2025
Doris Liu6432cd62013-06-13 17:20:31 -07002026/* Below is no longer needed, except to get rid of compile error
2027 * TODO: Remove these
2028 */
2029
2030 // TODO: Delete this function after old camera code is removed
2031 @Override
2032 public void onRestorePreferencesClicked() {}
2033
Michael Kolb8872c232013-01-29 10:33:22 -08002034}