blob: d1b01087b5f2a03f2ca7a069977fc7b1000f5e4e [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.annotation.TargetApi;
20import android.app.Activity;
Michael Kolb8872c232013-01-29 10:33:22 -080021import android.content.ContentResolver;
Angus Kong0d00a892013-03-26 11:40:40 -070022import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080023import android.content.Intent;
Michael Kolb8872c232013-01-29 10:33:22 -080024import android.graphics.Bitmap;
Angus Kong454d63f2014-05-06 14:45:59 -070025import android.graphics.BitmapFactory;
Michael Kolb8872c232013-01-29 10:33:22 -080026import android.graphics.SurfaceTexture;
27import android.hardware.Camera.CameraInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080028import android.hardware.Camera.Parameters;
Angus Kong0d00a892013-03-26 11:40:40 -070029import android.hardware.Sensor;
30import android.hardware.SensorEvent;
31import android.hardware.SensorEventListener;
32import android.hardware.SensorManager;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.location.Location;
Doris Liu6c751642014-05-05 18:43:26 -070034import android.media.AudioManager;
Michael Kolb8872c232013-01-29 10:33:22 -080035import android.media.CameraProfile;
Doris Liu6c751642014-05-05 18:43:26 -070036import android.media.SoundPool;
Michael Kolb8872c232013-01-29 10:33:22 -080037import android.net.Uri;
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -070038import android.os.AsyncTask;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070039import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080040import android.os.Bundle;
Michael Kolb8872c232013-01-29 10:33:22 -080041import android.os.Handler;
42import android.os.Looper;
43import android.os.Message;
44import android.os.MessageQueue;
45import android.os.SystemClock;
46import android.provider.MediaStore;
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;
Michael Kolb8872c232013-01-29 10:33:22 -080050
Sameer Padala2c8cc452013-11-05 18:49:12 -080051import com.android.camera.PhotoModule.NamedImages.NamedEntity;
52import com.android.camera.app.AppController;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080053import com.android.camera.app.CameraAppUI;
Angus Kong0b9eb5b2014-04-30 15:03:33 -070054import com.android.camera.app.CameraProvider;
Sascha Haeberlingf9cba4e2014-04-22 09:11:28 -070055import com.android.camera.app.LocationManager;
56import com.android.camera.app.MediaSaver;
57import com.android.camera.app.MemoryManager;
58import com.android.camera.app.MemoryManager.MemoryListener;
Angus Kong88289042014-04-22 16:39:42 -070059import com.android.camera.cameradevice.CameraCapabilities;
Doris Liucb8215e2014-05-19 11:53:32 -070060import com.android.camera.cameradevice.CameraManager;
Angus Kong1045fef2014-04-21 11:17:37 -070061import com.android.camera.cameradevice.CameraManager.CameraAFCallback;
62import com.android.camera.cameradevice.CameraManager.CameraAFMoveCallback;
63import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
64import com.android.camera.cameradevice.CameraManager.CameraProxy;
65import com.android.camera.cameradevice.CameraManager.CameraShutterCallback;
Angus Kong2bca2102014-03-11 16:27:30 -070066import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070067import com.android.camera.exif.ExifInterface;
68import com.android.camera.exif.ExifTag;
69import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080070import com.android.camera.hardware.HardwareSpec;
71import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080072import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080073import com.android.camera.remote.RemoteCameraModule;
Andy Huibers90c7ad52014-05-20 22:13:54 -070074import com.android.camera.settings.CameraPictureSizesCacher;
Angus Kong454d63f2014-05-06 14:45:59 -070075import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080076import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010077import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070078import com.android.camera.ui.CountDownView;
Angus Kongdcccc512013-08-08 17:06:03 -070079import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070080import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070081import com.android.camera.util.GcamHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070082import com.android.camera.util.SessionStatsCollector;
Angus Kong63424662014-04-23 10:47:47 -070083import com.android.camera.util.Size;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070084import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070085import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070086import com.android.camera2.R;
Seth Raphael5e09d012013-12-18 13:45:03 -080087import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080088
Angus Kong454d63f2014-05-06 14:45:59 -070089import java.io.ByteArrayOutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070090import java.io.File;
91import java.io.FileNotFoundException;
92import java.io.FileOutputStream;
93import java.io.IOException;
94import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010095import java.lang.ref.WeakReference;
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -070096import java.text.DecimalFormat;
Angus Kongdcccc512013-08-08 17:06:03 -070097import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070098import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070099
Michael Kolb8872c232013-01-29 10:33:22 -0800100public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800101 extends CameraModule
102 implements PhotoController,
103 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800104 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800105 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -0800106 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800107 SettingsManager.OnSettingChangedListener,
Doris Liu6c751642014-05-05 18:43:26 -0700108 RemoteCameraModule,
109 CountDownView.OnCountDownStatusListener {
Michael Kolb8872c232013-01-29 10:33:22 -0800110
Angus Kong2bca2102014-03-11 16:27:30 -0700111 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
Michael Kolb8872c232013-01-29 10:33:22 -0800112
113 // We number the request code from 1000 to avoid collision with Gallery.
114 private static final int REQUEST_CROP = 1000;
115
Angus Kong13e87c42013-11-25 10:02:47 -0800116 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800117 private static final int MSG_FIRST_TIME_INIT = 1;
118 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800119
120 // The subset of parameters we need to update in setCameraParameters().
121 private static final int UPDATE_PARAM_INITIALIZE = 1;
122 private static final int UPDATE_PARAM_ZOOM = 2;
123 private static final int UPDATE_PARAM_PREFERENCE = 4;
124 private static final int UPDATE_PARAM_ALL = -1;
125
Andy Huibersdef975d2013-11-22 09:13:39 -0800126 // This is the delay before we execute onResume tasks when coming
127 // from the lock screen, to allow time for onPause to execute.
128 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800129
Ruben Brunkd7488272013-10-10 18:45:53 -0700130 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
131
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700132 private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0");
133
Michael Kolb8872c232013-01-29 10:33:22 -0800134 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800135 private CameraProxy mCameraDevice;
136 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700137 private CameraCapabilities mCameraCapabilities;
Michael Kolb8872c232013-01-29 10:33:22 -0800138 private Parameters mParameters;
139 private boolean mPaused;
Doris Liu6a8ae812014-05-20 12:43:56 -0700140 private boolean mShouldSetPreviewCallbacks = ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK;
Michael Kolbd6954f32013-03-08 20:43:01 -0800141
142 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800143
Michael Kolb8872c232013-01-29 10:33:22 -0800144 // The activity is going to switch to the specified camera id. This is
145 // needed because texture copy is done in GL thread. -1 means camera is not
146 // switching.
147 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800148
149 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
150 // needed to be updated in mUpdateSet.
151 private int mUpdateSet;
152
153 private static final int SCREEN_DELAY = 2 * 60 * 1000;
154
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800155 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800156
157 private Parameters mInitialParams;
158 private boolean mFocusAreaSupported;
159 private boolean mMeteringAreaSupported;
160 private boolean mAeLockSupported;
161 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700162 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800163
164 // The degrees of the device rotated clockwise from its natural orientation.
165 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800166
167 private static final String sTempCropFilename = "crop-temp";
168
Michael Kolb8872c232013-01-29 10:33:22 -0800169 private boolean mFaceDetectionStarted = false;
170
Michael Kolb8872c232013-01-29 10:33:22 -0800171 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
172 private String mCropValue;
173 private Uri mSaveUri;
174
Ruben Brunkd217ed02013-10-08 23:31:13 -0700175 private Uri mDebugUri;
176
Angus Kongce5480e2013-01-29 17:43:48 -0800177 // We use a queue to generated names of the images to be used later
178 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800179 private NamedImages mNamedImages;
180
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800181 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800182 @Override
183 public void run() {
184 onShutterButtonClick();
185 }
186 };
187
Michael Kolb8872c232013-01-29 10:33:22 -0800188 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800189 * An unpublished intent flag requesting to return as soon as capturing is
190 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800191 */
192 private static final String EXTRA_QUICK_CAPTURE =
193 "android.intent.extra.quickCapture";
194
195 // The display rotation in degrees. This is only valid when mCameraState is
196 // not PREVIEW_STOPPED.
197 private int mDisplayRotation;
198 // The value for android.hardware.Camera.setDisplayOrientation.
199 private int mCameraDisplayOrientation;
200 // The value for UI components like indicators.
201 private int mDisplayOrientation;
202 // The value for android.hardware.Camera.Parameters.setRotation.
203 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700204 // Indicates whether we are using front camera
205 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800206 private boolean mFirstTimeInitialized;
207 private boolean mIsImageCaptureIntent;
208
Michael Kolb8872c232013-01-29 10:33:22 -0800209 private int mCameraState = PREVIEW_STOPPED;
210 private boolean mSnapshotOnIdle = false;
211
212 private ContentResolver mContentResolver;
213
214 private LocationManager mLocationManager;
Doris Liu2b906b82013-12-10 16:34:08 -0800215 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800216
Michael Kolb8872c232013-01-29 10:33:22 -0800217 private final PostViewPictureCallback mPostViewPictureCallback =
218 new PostViewPictureCallback();
219 private final RawPictureCallback mRawPictureCallback =
220 new RawPictureCallback();
221 private final AutoFocusCallback mAutoFocusCallback =
222 new AutoFocusCallback();
223 private final Object mAutoFocusMoveCallback =
224 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700225 ? new AutoFocusMoveCallback()
226 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800227
228 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
229
230 private long mFocusStartTime;
231 private long mShutterCallbackTime;
232 private long mPostViewPictureCallbackTime;
233 private long mRawPictureCallbackTime;
234 private long mJpegPictureCallbackTime;
235 private long mOnResumeTime;
236 private byte[] mJpegImageData;
237
238 // These latency time are for the CameraLatency test.
239 public long mAutoFocusTime;
240 public long mShutterLag;
241 public long mShutterToPictureDisplayedTime;
242 public long mPictureDisplayedToJpegCallbackTime;
243 public long mJpegCallbackFinishTime;
244 public long mCaptureStartTime;
245
246 // This handles everything about focus.
247 private FocusOverlayManager mFocusManager;
248
Doris Liubd1b8f92014-01-03 17:59:51 -0800249 private final int mGcamModeIndex;
Doris Liu6c751642014-05-05 18:43:26 -0700250 private final CountdownSoundPlayer mCountdownSoundPlayer = new CountdownSoundPlayer();
Doris Liubd1b8f92014-01-03 17:59:51 -0800251
Michael Kolb8872c232013-01-29 10:33:22 -0800252 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800253
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100254 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800255
Michael Kolb8872c232013-01-29 10:33:22 -0800256 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700257 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800258 private final float[] mGData = new float[3];
259 private final float[] mMData = new float[3];
260 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700261 private int mHeading = -1;
262
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800263 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700264 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700265
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800266 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800267 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800268 @Override
269 public void onMediaSaved(Uri uri) {
270 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700271 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800272 }
273 }
274 };
Angus Kong454d63f2014-05-06 14:45:59 -0700275 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800276
Angus Kong0b9eb5b2014-04-30 15:03:33 -0700277 private final Runnable mResumeTaskRunnable = new Runnable() {
278 @Override
279 public void run() {
280 onResumeTasks();
281 }
282 };
283
Doris Liudb8f9752014-05-12 15:25:13 -0700284 /**
285 * This callback gets called when user select whether or not to
286 * turn on geo-tagging.
287 */
288 public interface LocationDialogCallback {
289 /**
290 * Gets called after user selected/unselected geo-tagging feature.
291 *
292 * @param selected whether or not geo-tagging feature is selected
293 */
294 public void onLocationTaggingSelected(boolean selected);
295 }
296
297 /**
298 * This callback defines the text that is shown in the aspect ratio selection
299 * dialog, provides the current aspect ratio, and gets notified when user changes
300 * aspect ratio selection in the dialog.
301 */
302 public interface AspectRatioDialogCallback {
303 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700304 * Returns current aspect ratio that is being used to set as default.
305 */
306 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
307
308 /**
309 * Gets notified when user has made the aspect ratio selection.
310 *
311 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700312 * @param dialogHandlingFinishedRunnable runnable to run when the operations
313 * needed to handle changes from dialog
314 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700315 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700316 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
317 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700318 }
319
Angus Kongdcccc512013-08-08 17:06:03 -0700320 private void checkDisplayRotation() {
321 // Set the display orientation if display rotation has changed.
322 // Sometimes this happens when the device is held upside
323 // down and camera app is opened. Rotation animation will
324 // take some time and the rotation value we have got may be
325 // wrong. Framework does not have a callback for this now.
326 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
327 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800328 }
Angus Kongdcccc512013-08-08 17:06:03 -0700329 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
330 mHandler.postDelayed(new Runnable() {
331 @Override
332 public void run() {
333 checkDisplayRotation();
334 }
335 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800336 }
337 }
338
339 /**
340 * This Handler is used to post message back onto the main thread of the
341 * application
342 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100343 private static class MainHandler extends Handler {
344 private final WeakReference<PhotoModule> mModule;
345
346 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800347 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100348 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800349 }
350
Michael Kolb8872c232013-01-29 10:33:22 -0800351 @Override
352 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100353 PhotoModule module = mModule.get();
354 if (module == null) {
355 return;
356 }
Michael Kolb8872c232013-01-29 10:33:22 -0800357 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800358 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100359 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800360 break;
361 }
362
Angus Kong13e87c42013-11-25 10:02:47 -0800363 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100364 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800365 break;
366 }
Michael Kolb8872c232013-01-29 10:33:22 -0800367 }
368 }
369 }
370
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700371 private void switchToGcamCapture() {
372 if (mActivity != null && mGcamModeIndex != 0) {
373 SettingsManager settingsManager = mActivity.getSettingsManager();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700374 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700375 SettingsManager.VALUE_ON);
376
377 // Disable the HDR+ button to prevent callbacks from being
378 // queued before the correct callback is attached to the button
379 // in the new module. The new module will set the enabled/disabled
380 // of this button when the module's preferred camera becomes available.
381 ButtonManager buttonManager = mActivity.getButtonManager();
382 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
383
384 // Do not post this to avoid this module switch getting interleaved with
385 // other button callbacks.
386 mActivity.onModeSelected(mGcamModeIndex);
387 }
388 }
389
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800390 /**
391 * Constructs a new photo module.
392 */
Angus Kongc4e66562013-11-22 23:03:21 -0800393 public PhotoModule(AppController app) {
394 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800395 mGcamModeIndex = app.getAndroidContext().getResources()
396 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800397 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700398
Michael Kolb8872c232013-01-29 10:33:22 -0800399 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100400 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
401 mActivity = activity;
402 // TODO: Need to look at the controller interface to see if we can get
403 // rid of passing in the activity directly.
404 mAppController = mActivity;
405
406 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
407 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800408
409 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800410 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800411
Doris Liudb8f9752014-05-12 15:25:13 -0700412 // TODO: Move this to SettingsManager as a part of upgrade procedure.
413 if (!settingsManager.getBoolean(SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO)) {
414 // Switch to back camera to set aspect ratio.
415 mCameraId = Integer.parseInt(settingsManager
416 .getDefaultCameraIdSetting(activity).getDefault());
417 }
418
Michael Kolb8872c232013-01-29 10:33:22 -0800419 mContentResolver = mActivity.getContentResolver();
420
Michael Kolb8872c232013-01-29 10:33:22 -0800421 // Surface texture is from camera screen nail and startPreview needs it.
422 // This must be done before startPreview.
423 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800424
Angus Kong20fad242013-11-11 18:23:46 -0800425 mActivity.getCameraProvider().requestCamera(mCameraId);
426
Michael Kolb8872c232013-01-29 10:33:22 -0800427 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800428 mLocationManager = mActivity.getLocationManager();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800429 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700430 mUI.setCountdownFinishedListener(this);
431
432 // TODO: Make this a part of app controller API.
433 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
434 cancelButton.setOnClickListener(new View.OnClickListener() {
435 @Override
436 public void onClick(View view) {
437 cancelCountDown();
438 }
439 });
440 }
441
442 private void cancelCountDown() {
443 if (mUI.isCountingDown()) {
444 // Cancel on-going countdown.
445 mUI.cancelCountDown();
446 }
447 mAppController.getCameraAppUI().transitionToCapture();
448 mAppController.getCameraAppUI().showModeOptions();
Michael Kolbd6954f32013-03-08 20:43:01 -0800449 }
450
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800451 @Override
452 public boolean isUsingBottomBar() {
453 return true;
454 }
455
Michael Kolbd6954f32013-03-08 20:43:01 -0800456 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800457 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800458 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800459 setupCaptureParams();
460 }
461 }
462
463 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800464 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800465 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800466 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700467 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800468 }
469
Doris Liudb8f9752014-05-12 15:25:13 -0700470 /**
471 * Prompt the user to pick to record location and choose aspect ratio for the
472 * very first run of camera only.
473 */
474 private void settingsFirstRun() {
475 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100476
Doris Liudb8f9752014-05-12 15:25:13 -0700477 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800478 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800479 }
Doris Liudb8f9752014-05-12 15:25:13 -0700480
481 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION);
482 boolean aspectRatioPrompt = !settingsManager.getBoolean(
483 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO);
484 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800485 return;
486 }
Doris Liudb8f9752014-05-12 15:25:13 -0700487
Michael Kolb8872c232013-01-29 10:33:22 -0800488 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800489 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800490 if (backCameraId == -1) {
491 // If there is no back camera, do not show the prompt.
492 return;
493 }
Doris Liudb8f9752014-05-12 15:25:13 -0700494
495 if (locationPrompt) {
496 // Show both location and aspect ratio selection dialog.
497 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700498 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700499 public void onLocationTaggingSelected(boolean selected) {
500 settingsManager.setLocation(selected, mActivity.getLocationManager());
501 }
502 }, createAspectRatioDialogCallback());
503 } else {
504 // App upgrade. Only show aspect ratio selection.
505 mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
506 }
507 }
508
509 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
510 Size currentSize = new Size(mParameters.getPictureSize());
511 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
512 if (aspectRatio < 1f) {
513 aspectRatio = 1 / aspectRatio;
514 }
515 final AspectRatioSelector.AspectRatio currentAspectRatio;
516 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
517 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
518 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
519 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
520 } else {
521 // TODO: Log error and not show dialog.
522 return null;
523 }
524
525 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
526 List<Size> pictureSizes = ResolutionUtil
527 .getDisplayableSizesFromSupported(sizes, true);
528
529 // This logic below finds the largest resolution for each aspect ratio.
530 // TODO: Move this somewhere that can be shared with SettingsActivity
531 int aspectRatio4x3Resolution = 0;
532 int aspectRatio16x9Resolution = 0;
533 Size largestSize4x3 = new Size(0, 0);
534 Size largestSize16x9 = new Size(0, 0);
535 for (Size size : pictureSizes) {
536 float pictureAspectRatio = (float) size.width() / (float) size.height();
537 pictureAspectRatio = pictureAspectRatio < 1 ?
538 1f / pictureAspectRatio : pictureAspectRatio;
539 int resolution = size.width() * size.height();
540 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
541 if (resolution > aspectRatio4x3Resolution) {
542 aspectRatio4x3Resolution = resolution;
543 largestSize4x3 = size;
544 }
545 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
546 if (resolution > aspectRatio16x9Resolution) {
547 aspectRatio16x9Resolution = resolution;
548 largestSize16x9 = size;
549 }
550 }
551 }
Doris Liudb8f9752014-05-12 15:25:13 -0700552
Doris Liu08b0cdd2014-05-13 19:19:55 -0700553 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
554 final Size size4x3ToSelect = largestSize4x3;
555 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700556
Doris Liudb8f9752014-05-12 15:25:13 -0700557 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700558
559 @Override
560 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
561 return currentAspectRatio;
562 }
563
564 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700565 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
566 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700567 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700568 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700569 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
570 largestSize4x3Text);
571 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700572 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700573 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
574 largestSize16x9Text);
575 }
576 mActivity.getSettingsManager().setBoolean(
577 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO, true);
578 if (newAspectRatio != currentAspectRatio) {
Doris Liudb8f9752014-05-12 15:25:13 -0700579 stopPreview();
580 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700581 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
582 } else {
583 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700584 }
585 }
586 };
587 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700588 }
Michael Kolb8872c232013-01-29 10:33:22 -0800589
ztenghui7b265a62013-09-09 14:58:44 -0700590 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700591 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800592 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700593 }
594
595 @Override
596 public void onPreviewUIDestroyed() {
597 if (mCameraDevice == null) {
598 return;
599 }
600 mCameraDevice.setPreviewTexture(null);
601 stopPreview();
602 }
603
Doris Liu1dfe7822013-12-12 00:02:08 -0800604 @Override
605 public void startPreCaptureAnimation() {
606 mAppController.startPreCaptureAnimation();
607 }
608
Michael Kolbd6954f32013-03-08 20:43:01 -0800609 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800610 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800611 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800612 }
613
Michael Kolbd6954f32013-03-08 20:43:01 -0800614 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800615 if (mPaused) {
616 return;
617 }
Doris Liu6c751642014-05-05 18:43:26 -0700618 cancelCountDown();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800619 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800620
Alan Newbergerd41766f2014-04-09 18:25:34 -0700621 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800622 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800623 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800624 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800625 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800626 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800627 if (mFocusManager != null) {
628 mFocusManager.removeMessages();
629 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800630
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700631 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700632 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700633 // Start switch camera animation. Post a message because
634 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800635 }
636
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800637 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100638 new ButtonManager.ButtonCallback() {
639 @Override
640 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800641 // At the time this callback is fired, the camera id
642 // has be set to the desired camera.
643
Angus Kong97e282a2014-03-04 18:44:49 -0800644 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100645 return;
646 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800647 // If switching to back camera, and HDR+ is still on,
648 // switch back to gcam, otherwise handle callback normally.
649 SettingsManager settingsManager = mActivity.getSettingsManager();
650 if (settingsManager.isCameraBackFacing()) {
651 if (settingsManager.requestsReturnToHdrPlus()) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700652 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800653 return;
654 }
655 }
656
Sascha Haeberlingde303232014-02-07 02:30:53 +0100657 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800658
Alan Newbergerd41766f2014-04-09 18:25:34 -0700659 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100660 // We need to keep a preview frame for the animation before
661 // releasing the camera. This will trigger
662 // onPreviewTextureCopied.
663 // TODO: Need to animate the camera switch
664 switchCamera();
665 }
666 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800667
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800668 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100669 new ButtonManager.ButtonCallback() {
670 @Override
671 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700672 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100673 if (GcamHelper.hasGcamCapture()) {
674 // Set the camera setting to default backfacing.
Sascha Haeberlingde303232014-02-07 02:30:53 +0100675 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700676 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100677 } else {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700678 if (settingsManager.isHdrOn()) {
679 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
680 CameraUtil.SCENE_MODE_HDR);
681 } else {
682 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
683 Parameters.SCENE_MODE_AUTO);
684 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100685 updateParametersSceneMode();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700686 mCameraDevice.setParameters(mParameters);
687 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100688 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800689 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100690 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800691
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800692 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
693 @Override
694 public void onClick(View v) {
695 onCaptureCancelled();
696 }
697 };
698
699 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
700 @Override
701 public void onClick(View v) {
702 onCaptureDone();
703 }
704 };
705
706 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
707 @Override
708 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800709 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800710 onCaptureRetake();
711 }
712 };
713
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800714 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700715 public void hardResetSettings(SettingsManager settingsManager) {
716 // PhotoModule should hard reset HDR+ to off.
717 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
718 }
719
720 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800721 public HardwareSpec getHardwareSpec() {
Erin Dahlgren49ab9222014-01-28 17:40:28 -0800722 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800723 }
724
725 @Override
726 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
727 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
728
729 bottomBarSpec.enableCamera = true;
730 bottomBarSpec.cameraCallback = mCameraCallback;
731 bottomBarSpec.enableFlash = true;
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800732 bottomBarSpec.enableHdr = true;
733 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800734 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700735 if (mCameraCapabilities != null) {
736 bottomBarSpec.enableExposureCompensation = true;
737 bottomBarSpec.exposureCompensationSetCallback =
738 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
739 @Override
740 public void setExposure(int value) {
741 setExposureCompensation(value);
742 }
743 };
744 bottomBarSpec.minExposureCompensation =
745 mCameraCapabilities.getMinExposureCompensation();
746 bottomBarSpec.maxExposureCompensation =
747 mCameraCapabilities.getMaxExposureCompensation();
748 bottomBarSpec.exposureCompensationStep =
749 mCameraCapabilities.getExposureCompensationStep();
750 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800751
Doris Liu6c751642014-05-05 18:43:26 -0700752 bottomBarSpec.enableSelfTimer = true;
753
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800754 if (isImageCaptureIntent()) {
755 bottomBarSpec.showCancel = true;
756 bottomBarSpec.cancelCallback = mCancelCallback;
757 bottomBarSpec.showDone = true;
758 bottomBarSpec.doneCallback = mDoneCallback;
759 bottomBarSpec.showRetake = true;
760 bottomBarSpec.retakeCallback = mRetakeCallback;
761 }
762
763 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800764 }
765
Michael Kolbd6954f32013-03-08 20:43:01 -0800766 // either open a new camera or switch cameras
767 private void openCameraCommon() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800768 mUI.onCameraOpened(mParameters);
Angus Kong0fb819b2013-10-08 13:44:19 -0700769 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800770 // Set hdr plus to default: off.
771 SettingsManager settingsManager = mActivity.getSettingsManager();
772 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700773 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800774 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800775 }
776
ztenghui7b265a62013-09-09 14:58:44 -0700777 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800778 public void updatePreviewAspectRatio(float aspectRatio) {
779 mAppController.updatePreviewAspectRatio(aspectRatio);
780 }
781
Michael Kolb8872c232013-01-29 10:33:22 -0800782 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800783 SettingsManager settingsManager = mActivity.getSettingsManager();
784 if (settingsManager == null) {
785 Log.e(TAG, "Settings manager is null!");
786 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800787 }
Spike Spragueabf54e22014-03-27 15:41:28 -0700788 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
Michael Kolb8872c232013-01-29 10:33:22 -0800789 }
790
Michael Kolb8872c232013-01-29 10:33:22 -0800791 // Snapshots can only be taken after this is called. It should be called
792 // once only. We could have done these things in onCreate() but we want to
793 // make preview screen appear as soon as possible.
794 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700795 if (mFirstTimeInitialized || mPaused) {
796 return;
797 }
Michael Kolb8872c232013-01-29 10:33:22 -0800798
Michael Kolbd6954f32013-03-08 20:43:01 -0800799 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800800
Angus Kong86d36312013-01-31 18:22:44 -0800801 // We set the listener only when both service and shutterbutton
802 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800803 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800804
Michael Kolb8872c232013-01-29 10:33:22 -0800805 mNamedImages = new NamedImages();
806
807 mFirstTimeInitialized = true;
808 addIdleHandler();
809
Spike Spraguee6374b72014-04-25 17:24:32 -0700810 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800811 }
812
Michael Kolbd6954f32013-03-08 20:43:01 -0800813 // If the activity is paused and resumed, this method will be called in
814 // onResume.
815 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800816 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800817 mNamedImages = new NamedImages();
818 mUI.initializeSecondTime(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -0800819 }
820
Michael Kolb8872c232013-01-29 10:33:22 -0800821 private void addIdleHandler() {
822 MessageQueue queue = Looper.myQueue();
823 queue.addIdleHandler(new MessageQueue.IdleHandler() {
824 @Override
825 public boolean queueIdle() {
826 Storage.ensureOSXCompatible();
827 return false;
828 }
829 });
830 }
831
Michael Kolb8872c232013-01-29 10:33:22 -0800832 @Override
833 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800834 if (mFaceDetectionStarted) {
835 return;
836 }
Angus Kong88289042014-04-22 16:39:42 -0700837 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800838 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700839 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700840 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800841 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700842 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800843 }
844 }
845
Michael Kolb8872c232013-01-29 10:33:22 -0800846 @Override
847 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800848 if (!mFaceDetectionStarted) {
849 return;
850 }
Angus Kong88289042014-04-22 16:39:42 -0700851 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800852 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700853 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800854 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800855 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700856 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800857 }
858 }
859
Michael Kolb8872c232013-01-29 10:33:22 -0800860 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700861 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700862
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800863 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700864
Sascha Haeberling37f36112013-08-06 14:31:52 -0700865 public ShutterCallback(boolean needsAnimation) {
866 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700867 }
868
Michael Kolb8872c232013-01-29 10:33:22 -0800869 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700870 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800871 mShutterCallbackTime = System.currentTimeMillis();
872 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
873 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700874 if (mNeedsAnimation) {
875 mActivity.runOnUiThread(new Runnable() {
876 @Override
877 public void run() {
878 animateAfterShutter();
879 }
880 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700881 }
Michael Kolb8872c232013-01-29 10:33:22 -0800882 }
883 }
884
Angus Kong9ef99252013-07-18 18:04:19 -0700885 private final class PostViewPictureCallback
886 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800887 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800888 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800889 mPostViewPictureCallbackTime = System.currentTimeMillis();
890 Log.v(TAG, "mShutterToPostViewCallbackTime = "
891 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
892 + "ms");
893 }
894 }
895
Angus Kong9ef99252013-07-18 18:04:19 -0700896 private final class RawPictureCallback
897 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800898 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800899 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800900 mRawPictureCallbackTime = System.currentTimeMillis();
901 Log.v(TAG, "mShutterToRawCallbackTime = "
902 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
903 }
904 }
905
Angus Kong454d63f2014-05-06 14:45:59 -0700906 private static class ResizeBundle {
907 byte[] jpegData;
908 float targetAspectRatio;
909 ExifInterface exif;
910 }
911
912 /**
913 * @return Cropped image if the target aspect ratio is larger than the jpeg
914 * aspect ratio on the long axis. The original jpeg otherwise.
915 */
916 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
917
918 final byte[] jpegData = dataBundle.jpegData;
919 final ExifInterface exif = dataBundle.exif;
920 float targetAspectRatio = dataBundle.targetAspectRatio;
921
922 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
923 int originalWidth = original.getWidth();
924 int originalHeight = original.getHeight();
925 int newWidth;
926 int newHeight;
927
928 if (originalWidth > originalHeight) {
929 newHeight = (int) (originalWidth / targetAspectRatio);
930 newWidth = originalWidth;
931 } else {
932 newWidth = (int) (originalHeight / targetAspectRatio);
933 newHeight = originalHeight;
934 }
935 int xOffset = (originalWidth - newWidth)/2;
936 int yOffset = (originalHeight - newHeight)/2;
937
938 if (xOffset < 0 || yOffset < 0) {
939 return dataBundle;
940 }
941
942 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
943 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
944 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
945
946 ByteArrayOutputStream stream = new ByteArrayOutputStream();
947
948 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
949 dataBundle.jpegData = stream.toByteArray();
950 return dataBundle;
951 }
952
Angus Kong9ef99252013-07-18 18:04:19 -0700953 private final class JpegPictureCallback
954 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800955 Location mLocation;
956
957 public JpegPictureCallback(Location loc) {
958 mLocation = loc;
959 }
960
961 @Override
Angus Kong454d63f2014-05-06 14:45:59 -0700962 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700963 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800964 if (mPaused) {
965 return;
966 }
Doris Liu6432cd62013-06-13 17:20:31 -0700967 if (mIsImageCaptureIntent) {
968 stopPreview();
969 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700970 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700971 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800972 }
973
974 mJpegPictureCallbackTime = System.currentTimeMillis();
975 // If postview callback has arrived, the captured image is displayed
976 // in postview callback. If not, the captured image is displayed in
977 // raw picture callback.
978 if (mPostViewPictureCallbackTime != 0) {
979 mShutterToPictureDisplayedTime =
980 mPostViewPictureCallbackTime - mShutterCallbackTime;
981 mPictureDisplayedToJpegCallbackTime =
982 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
983 } else {
984 mShutterToPictureDisplayedTime =
985 mRawPictureCallbackTime - mShutterCallbackTime;
986 mPictureDisplayedToJpegCallbackTime =
987 mJpegPictureCallbackTime - mRawPictureCallbackTime;
988 }
989 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
990 + mPictureDisplayedToJpegCallbackTime + "ms");
991
Michael Kolb8872c232013-01-29 10:33:22 -0800992 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
993 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700994 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800995 }
996
Angus Kong454d63f2014-05-06 14:45:59 -0700997 long now = System.currentTimeMillis();
998 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
999 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1000 mJpegPictureCallbackTime = 0;
1001
1002 final ExifInterface exif = Exif.getExif(originalJpegData);
1003
1004 if (mShouldResizeTo16x9) {
1005 final ResizeBundle dataBundle = new ResizeBundle();
1006 dataBundle.jpegData = originalJpegData;
1007 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1008 dataBundle.exif = exif;
1009 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1010
1011 @Override
1012 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1013 return cropJpegDataToAspectRatio(resizeBundles[0]);
1014 }
1015
1016 @Override
1017 protected void onPostExecute(ResizeBundle result) {
1018 saveFinalPhoto(result.jpegData, result.exif, camera);
1019 }
1020 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1021
1022 } else {
1023 saveFinalPhoto(originalJpegData, exif, camera);
1024 }
1025 }
1026
1027 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1028
Doris Liu36e56fb2013-09-11 17:38:08 -07001029 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001030
Andy Huiberse08bc042014-04-11 19:26:47 -07001031 float zoomValue = 0f;
Angus Kong88289042014-04-22 16:39:42 -07001032 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Andy Huiberse08bc042014-04-11 19:26:47 -07001033 int zoomIndex = mParameters.getZoom();
1034 List<Integer> zoomRatios = mParameters.getZoomRatios();
1035 if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
1036 zoomValue = 0.01f * zoomRatios.get(zoomIndex);
1037 }
1038 }
Angus Kong454d63f2014-05-06 14:45:59 -07001039
Andy Huibers6b9743a2014-04-03 23:23:29 -07001040 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
Andy Huibers203abe52014-05-19 13:59:01 -07001041 String flashSetting =
1042 mActivity.getSettingsManager().get(SettingsManager.SETTING_FLASH_MODE);
1043 boolean gridLinesOn = mActivity.getSettingsManager().areGridLinesOn();
Andy Huibers10c58162014-03-29 14:06:54 -07001044 UsageStatistics.instance().photoCaptureDoneEvent(
1045 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1046 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers203abe52014-05-19 13:59:01 -07001047 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn);
Ruben Brunkd217ed02013-10-08 23:31:13 -07001048
Ruben Brunkd7488272013-10-10 18:45:53 -07001049 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001050 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001051 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1052 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001053 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001054 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1055 width = exifWidth;
1056 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001057 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001058 Size s;
1059 s = new Size(mParameters.getPictureSize());
1060 if ((mJpegRotation + orientation) % 180 == 0) {
1061 width = s.width();
1062 height = s.height();
1063 } else {
1064 width = s.height();
1065 height = s.width();
1066 }
Michael Kolb8872c232013-01-29 10:33:22 -08001067 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001068 NamedEntity name = mNamedImages.getNextNameEntity();
1069 String title = (name == null) ? null : name.title;
1070 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001071
1072 // Handle debug mode outputs
1073 if (mDebugUri != null) {
1074 // If using a debug uri, save jpeg there.
1075 saveToDebugUri(jpegData);
1076
1077 // Adjust the title of the debug image shown in mediastore.
1078 if (title != null) {
1079 title = DEBUG_IMAGE_PREFIX + title;
1080 }
1081 }
1082
Michael Kolb8872c232013-01-29 10:33:22 -08001083 if (title == null) {
1084 Log.e(TAG, "Unbalanced name/data pair");
1085 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001086 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001087 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001088 }
Angus Kong0d00a892013-03-26 11:40:40 -07001089 if (mHeading >= 0) {
1090 // heading direction has been updated by the sensor.
1091 ExifTag directionRefTag = exif.buildTag(
1092 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1093 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1094 ExifTag directionTag = exif.buildTag(
1095 ExifInterface.TAG_GPS_IMG_DIRECTION,
1096 new Rational(mHeading, 1));
1097 exif.setTag(directionRefTag);
1098 exif.setTag(directionTag);
1099 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001100 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001101 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001102 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001103 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001104 // Animate capture with real jpeg data instead of a preview
1105 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001106 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001107 } else {
1108 mJpegImageData = jpegData;
1109 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001110 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001111 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001112 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001113 }
1114 }
1115
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001116 // Send the taken photo to remote shutter listeners, if any are
1117 // registered.
1118 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
1119 @Override
1120 public void run() {
1121 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1122 }
1123 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001124
Michael Kolb8872c232013-01-29 10:33:22 -08001125 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001126 // latency. It's true that someone else could write to the SD card
1127 // in the mean time and fill it, but that could have happened
1128 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001129 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001130 }
1131 }
1132
Angus Kong9ef99252013-07-18 18:04:19 -07001133 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001134 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001135 public void onAutoFocus(boolean focused, CameraProxy camera) {
1136 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001137 if (mPaused) {
1138 return;
1139 }
Michael Kolb8872c232013-01-29 10:33:22 -08001140
1141 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001142 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001143 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001144 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001145 }
1146 }
1147
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001148 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001149 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001150 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001151 @Override
1152 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001153 boolean moving, CameraProxy camera) {
1154 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001155 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001156 }
1157 }
1158
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001159 /**
1160 * This class is just a thread-safe queue for name,date holder objects.
1161 */
1162 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001163 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001164
1165 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001166 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001167 }
1168
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001169 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001170 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001171 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001172 r.date = date;
1173 mQueue.add(r);
1174 }
1175
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001176 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001177 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001178 if (!mQueue.isEmpty()) {
1179 return mQueue.remove(0);
1180 }
Michael Kolb8872c232013-01-29 10:33:22 -08001181 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001182 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001183 }
1184
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001185 public static class NamedEntity {
1186 public String title;
1187 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001188 }
1189 }
1190
1191 private void setCameraState(int state) {
1192 mCameraState = state;
1193 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001194 case PREVIEW_STOPPED:
1195 case SNAPSHOT_IN_PROGRESS:
1196 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001197 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001198 break;
1199 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001200 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001201 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001202 }
1203 }
1204
Sascha Haeberling37f36112013-08-06 14:31:52 -07001205 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001206 // Only animate when in full screen capture mode
1207 // i.e. If monkey/a user swipes to the gallery during picture taking,
1208 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001209 if (!mIsImageCaptureIntent) {
1210 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001211 }
Michael Kolb8872c232013-01-29 10:33:22 -08001212 }
1213
1214 @Override
1215 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001216 // If we are already in the middle of taking a snapshot or the image
1217 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001218 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001219 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001220 return false;
1221 }
1222 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001223
Michael Kolb8872c232013-01-29 10:33:22 -08001224 mPostViewPictureCallbackTime = 0;
1225 mJpegImageData = null;
1226
Angus Kongb50b5cb2013-08-09 14:55:20 -07001227 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001228
1229 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001230 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001231 }
1232
1233 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -08001234 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001235
Doris Liu3cf565c2013-02-15 10:55:37 -08001236 // We need to be consistent with the framework orientation (i.e. the
1237 // orientation of the UI.) when the auto-rotate screen setting is on.
1238 if (mActivity.isAutoRotateScreen()) {
1239 orientation = (360 - mDisplayRotation) % 360;
1240 } else {
1241 orientation = mOrientation;
1242 }
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +01001243 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
1244 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001245 mParameters.setRotation(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001246 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001247 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -08001248 mCameraDevice.setParameters(mParameters);
1249
Sascha Haeberling88901942013-08-28 17:49:00 -07001250 // We don't want user to press the button again while taking a
1251 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001252 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001253 mCameraDevice.takePicture(mHandler,
1254 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001255 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001256 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001257
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001258 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001259
1260 mFaceDetectionStarted = false;
1261 setCameraState(SNAPSHOT_IN_PROGRESS);
1262 return true;
1263 }
1264
1265 @Override
1266 public void setFocusParameters() {
1267 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1268 }
1269
Michael Kolbd6954f32013-03-08 20:43:01 -08001270 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001271 // If scene mode is set, we cannot set flash mode, white balance, and
1272 // focus mode, instead, we read it from driver
1273 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001274 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001275 }
1276 }
1277
Doris Liu8ad8ad42014-03-27 14:43:31 -07001278 private void overrideCameraSettings(final String flashMode, final String focusMode) {
Erin Dahlgrene419b192013-12-03 13:10:27 -08001279 SettingsManager settingsManager = mActivity.getSettingsManager();
1280 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
Erin Dahlgrene419b192013-12-03 13:10:27 -08001281 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001282 }
1283
1284 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001285 public void onOrientationChanged(int orientation) {
1286 // We keep the last known orientation. So if the user first orient
1287 // the camera then point the camera to floor or sky, we still have
1288 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001289 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1290 return;
1291 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001292 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001293 }
1294
1295 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001296 public void onCameraAvailable(CameraProxy cameraProxy) {
1297 if (mPaused) {
1298 return;
1299 }
1300 mCameraDevice = cameraProxy;
1301
1302 initializeCapabilities();
1303
1304 // Reset zoom value index.
1305 mZoomValue = 0;
1306 if (mFocusManager == null) {
1307 initializeFocusManager();
1308 }
Angus Kong88289042014-04-22 16:39:42 -07001309 mFocusManager.setParameters(mInitialParams, mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001310
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001311 // Do camera parameter dependent initialization.
Angus Kong20fad242013-11-11 18:23:46 -08001312 mParameters = mCameraDevice.getParameters();
1313 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001314 // Set a listener which updates camera parameters based
1315 // on changed settings.
1316 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001317 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001318 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001319
Angus Kong20fad242013-11-11 18:23:46 -08001320 startPreview();
1321
1322 onCameraOpened();
1323 }
1324
1325 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001326 public void onCaptureCancelled() {
1327 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1328 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001329 }
1330
Michael Kolbd6954f32013-03-08 20:43:01 -08001331 @Override
1332 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001333 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001334 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001335 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001336 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001337 setupPreview();
1338 }
1339
Michael Kolbd6954f32013-03-08 20:43:01 -08001340 @Override
1341 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001342 if (mPaused) {
1343 return;
1344 }
1345
1346 byte[] data = mJpegImageData;
1347
1348 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001349 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001350 // caller specifies a "save uri" then write the data to its
1351 // stream. Otherwise, pass back a scaled down version of the bitmap
1352 // directly in the extras.
1353 if (mSaveUri != null) {
1354 OutputStream outputStream = null;
1355 try {
1356 outputStream = mContentResolver.openOutputStream(mSaveUri);
1357 outputStream.write(data);
1358 outputStream.close();
1359
1360 mActivity.setResultEx(Activity.RESULT_OK);
1361 mActivity.finish();
1362 } catch (IOException ex) {
1363 // ignore exception
1364 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001365 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001366 }
1367 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001368 ExifInterface exif = Exif.getExif(data);
1369 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001370 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1371 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001372 mActivity.setResultEx(Activity.RESULT_OK,
1373 new Intent("inline-data").putExtra("data", bitmap));
1374 mActivity.finish();
1375 }
1376 } else {
1377 // Save the image to a temp file and invoke the cropper
1378 Uri tempUri = null;
1379 FileOutputStream tempStream = null;
1380 try {
1381 File path = mActivity.getFileStreamPath(sTempCropFilename);
1382 path.delete();
1383 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1384 tempStream.write(data);
1385 tempStream.close();
1386 tempUri = Uri.fromFile(path);
1387 } catch (FileNotFoundException ex) {
1388 mActivity.setResultEx(Activity.RESULT_CANCELED);
1389 mActivity.finish();
1390 return;
1391 } catch (IOException ex) {
1392 mActivity.setResultEx(Activity.RESULT_CANCELED);
1393 mActivity.finish();
1394 return;
1395 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001396 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001397 }
1398
1399 Bundle newExtras = new Bundle();
1400 if (mCropValue.equals("circle")) {
1401 newExtras.putString("circleCrop", "true");
1402 }
1403 if (mSaveUri != null) {
1404 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1405 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001406 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001407 }
1408 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001409 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001410 }
1411
Sascha Haeberling37f36112013-08-06 14:31:52 -07001412 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001413 final String CROP_ACTION = "com.android.camera.action.CROP";
1414 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001415
1416 cropIntent.setData(tempUri);
1417 cropIntent.putExtras(newExtras);
1418
1419 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1420 }
1421 }
1422
Michael Kolb8872c232013-01-29 10:33:22 -08001423 @Override
1424 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001425 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001426 }
1427
1428 @Override
1429 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001430 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001431 || (mCameraState == PREVIEW_STOPPED)) {
1432 return;
1433 }
Michael Kolb8872c232013-01-29 10:33:22 -08001434
1435 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001436 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001437 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001438 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001439 return;
1440 }
Alan Newbergerd41766f2014-04-09 18:25:34 -07001441 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
Michael Kolb8872c232013-01-29 10:33:22 -08001442
Doris Liu6c751642014-05-05 18:43:26 -07001443 int countDownDuration = Integer.parseInt(mActivity.getSettingsManager()
1444 .get(SettingsManager.SETTING_COUNTDOWN_DURATION));
1445 if (countDownDuration > 0) {
1446 // Start count down.
1447 mAppController.getCameraAppUI().transitionToCancel();
1448 mAppController.getCameraAppUI().hideModeOptions();
1449 mUI.startCountdown(countDownDuration);
1450 return;
1451 } else {
1452 focusAndCapture();
1453 }
1454 }
1455
1456 private void focusAndCapture() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001457 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001458 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001459 }
Michael Kolb8872c232013-01-29 10:33:22 -08001460 // If the user wants to do a snapshot while the previous one is still
1461 // in progress, remember the fact and do it after we finish the previous
1462 // one and re-start the preview. Snapshot in progress also includes the
1463 // state that autofocus is focusing and a picture will be taken when
1464 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001465 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1466 if (!mIsImageCaptureIntent) {
1467 mSnapshotOnIdle = true;
1468 }
Michael Kolb8872c232013-01-29 10:33:22 -08001469 return;
1470 }
1471
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001472 mSnapshotOnIdle = false;
Angus Kongeaaf56d2014-02-20 11:59:10 -08001473 mFocusManager.focusAndCapture();
Michael Kolb8872c232013-01-29 10:33:22 -08001474 }
1475
Doris Liu6c751642014-05-05 18:43:26 -07001476 @Override
1477 public void onRemainingSecondsChanged(int remainingSeconds) {
1478 mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
1479 }
1480
1481 @Override
1482 public void onCountDownFinished() {
1483 mAppController.getCameraAppUI().transitionToCapture();
1484 mAppController.getCameraAppUI().showModeOptions();
1485 if (mPaused) {
1486 return;
1487 }
1488 focusAndCapture();
1489 }
1490
Andy Huibersdef975d2013-11-22 09:13:39 -08001491 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001492 if (mPaused) {
1493 return;
1494 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001495 Log.v(TAG, "Executing onResumeTasks.");
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001496 CameraProvider camProvider = mActivity.getCameraProvider();
1497 if (camProvider == null) {
1498 // No camera provider, the Activity is destroyed already.
1499 return;
1500 }
1501 camProvider.requestCamera(mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -08001502
Michael Kolb8872c232013-01-29 10:33:22 -08001503 mJpegPictureCallbackTime = 0;
1504 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001505
1506 mOnResumeTime = SystemClock.uptimeMillis();
1507 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001508
1509 // If first time initialization is not finished, put it in the
1510 // message queue.
1511 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001512 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001513 } else {
1514 initializeSecondTime();
1515 }
Michael Kolb8872c232013-01-29 10:33:22 -08001516
Angus Kong0d00a892013-03-26 11:40:40 -07001517 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1518 if (gsensor != null) {
1519 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1520 }
1521
1522 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1523 if (msensor != null) {
1524 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1525 }
Michael Kolb8872c232013-01-29 10:33:22 -08001526 }
1527
Angus Kongc4e66562013-11-22 23:03:21 -08001528 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001529 * @return Whether the currently active camera is front-facing.
1530 */
1531 private boolean isCameraFrontFacing() {
1532 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1533 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1534 }
1535
1536 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001537 * The focus manager is the first UI related element to get initialized, and
1538 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001539 */
1540 private void initializeFocusManager() {
1541 // Create FocusManager object. startPreview needs it.
1542 // if mFocusManager not null, reuse it
1543 // otherwise create a new instance
1544 if (mFocusManager != null) {
1545 mFocusManager.removeMessages();
1546 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001547 mMirror = isCameraFrontFacing();
Angus Kongc4e66562013-11-22 23:03:21 -08001548 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1549 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001550 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1551 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001552 mInitialParams, this, mMirror,
Doris Liu482de022013-12-18 19:18:16 -08001553 mActivity.getMainLooper(), mUI.getFocusUI());
Angus Kongc4e66562013-11-22 23:03:21 -08001554 }
Doris Liu482de022013-12-18 19:18:16 -08001555 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001556 }
1557
1558 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001559 public void resume() {
1560 mPaused = false;
Doris Liu6c751642014-05-05 18:43:26 -07001561 mCountdownSoundPlayer.loadSounds();
Doris Liu482de022013-12-18 19:18:16 -08001562 if (mFocusManager != null) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001563 // If camera is not open when resume is called, focus manager will
1564 // not
Doris Liu15b99612013-12-21 11:32:28 -08001565 // be initialized yet, in which case it will start listening to
1566 // preview area size change later in the initialization.
Doris Liu482de022013-12-18 19:18:16 -08001567 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1568 }
Doris Liu6c751642014-05-05 18:43:26 -07001569 mAppController.addPreviewAreaSizeChangedListener(mUI);
Doris Liu59401042014-01-14 17:51:32 -08001570
Angus Kongc4e66562013-11-22 23:03:21 -08001571 // Add delay on resume from lock screen only, in order to to speed up
1572 // the onResume --> onPause --> onResume cycle from lock screen.
1573 // Don't do always because letting go of thread can cause delay.
1574 String action = mActivity.getIntent().getAction();
1575 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1576 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1577 Log.v(TAG, "On resume, from lock screen.");
1578 // Note: onPauseAfterSuper() will delete this runnable, so we will
1579 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001580 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001581 } else {
1582 Log.v(TAG, "On resume.");
1583 onResumeTasks();
1584 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001585 getServices().getRemoteShutterListener().onModuleReady(this);
Andy Huibers10c58162014-03-29 14:06:54 -07001586 SessionStatsCollector.instance().sessionActive(true);
Angus Kongc4e66562013-11-22 23:03:21 -08001587 }
1588
1589 @Override
1590 public void pause() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001591 getServices().getRemoteShutterListener().onModuleExit();
Michael Kolb8872c232013-01-29 10:33:22 -08001592 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001593 mHandler.removeCallbacks(mResumeTaskRunnable);
Andy Huibers10c58162014-03-29 14:06:54 -07001594 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001595
Angus Kong0d00a892013-03-26 11:40:40 -07001596 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1597 if (gsensor != null) {
1598 mSensorManager.unregisterListener(this, gsensor);
1599 }
1600
1601 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1602 if (msensor != null) {
1603 mSensorManager.unregisterListener(this, msensor);
1604 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001605
Michael Kolb8872c232013-01-29 10:33:22 -08001606 // Reset the focus first. Camera CTS does not guarantee that
1607 // cancelAutoFocus is allowed after preview stops.
1608 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1609 mCameraDevice.cancelAutoFocus();
1610 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001611
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001612 // If the camera has not been opened asynchronously yet,
1613 // and startPreview hasn't been called, then this is a no-op.
1614 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001615 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001616 cancelCountDown();
1617 mCountdownSoundPlayer.release();
Michael Kolb8872c232013-01-29 10:33:22 -08001618
Angus Kongce5480e2013-01-29 17:43:48 -08001619 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001620 // If we are in an image capture intent and has taken
1621 // a picture, we just clear it in onPause.
1622 mJpegImageData = null;
1623
Angus Kongdcccc512013-08-08 17:06:03 -07001624 // Remove the messages and runnables in the queue.
1625 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001626
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001627 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001628 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001629 mUI.onPause();
1630
Michael Kolb8872c232013-01-29 10:33:22 -08001631 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001632 if (mFocusManager != null) {
1633 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001634 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001635 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001636 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001637 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001638
1639 SettingsManager settingsManager = mActivity.getSettingsManager();
1640 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001641 }
1642
Angus Kong20fad242013-11-11 18:23:46 -08001643 @Override
1644 public void destroy() {
1645 // TODO: implement this.
1646 }
1647
1648 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001649 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001650 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001651 }
1652
1653 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001654 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001655 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001656 setDisplayOrientation();
1657 }
1658 }
1659
Michael Kolb8872c232013-01-29 10:33:22 -08001660 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001661 return isCameraIdle()
1662 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001663 }
1664
1665 @Override
1666 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001667 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001668 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001669 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001670 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001671 setCameraState(FOCUSING);
1672 }
1673
1674 @Override
1675 public void cancelAutoFocus() {
1676 mCameraDevice.cancelAutoFocus();
1677 setCameraState(IDLE);
1678 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1679 }
1680
Michael Kolb8872c232013-01-29 10:33:22 -08001681 @Override
1682 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001683 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1684 || mCameraState == SNAPSHOT_IN_PROGRESS
1685 || mCameraState == SWITCHING_CAMERA
1686 || mCameraState == PREVIEW_STOPPED) {
1687 return;
1688 }
1689
1690 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001691 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1692 return;
1693 }
Doris Liu482de022013-12-18 19:18:16 -08001694 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001695 }
1696
1697 @Override
1698 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001699 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001700 }
1701
1702 @Override
1703 public boolean onKeyDown(int keyCode, KeyEvent event) {
1704 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001705 case KeyEvent.KEYCODE_VOLUME_UP:
1706 case KeyEvent.KEYCODE_VOLUME_DOWN:
1707 case KeyEvent.KEYCODE_FOCUS:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001708 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001709 if (event.getRepeatCount() == 0) {
1710 onShutterButtonFocus(true);
1711 }
1712 return true;
1713 }
1714 return false;
1715 case KeyEvent.KEYCODE_CAMERA:
1716 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1717 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001718 }
1719 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001720 case KeyEvent.KEYCODE_DPAD_CENTER:
1721 // If we get a dpad center event without any focused view, move
1722 // the focus to the shutter button and press it.
1723 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1724 // Start auto-focus immediately to reduce shutter lag. After
1725 // the shutter button gets the focus, onShutterButtonFocus()
1726 // will be called again but it is fine.
1727 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001728 }
1729 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001730 }
1731 return false;
1732 }
1733
1734 @Override
1735 public boolean onKeyUp(int keyCode, KeyEvent event) {
1736 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001737 case KeyEvent.KEYCODE_VOLUME_UP:
1738 case KeyEvent.KEYCODE_VOLUME_DOWN:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001739 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001740 onShutterButtonClick();
1741 return true;
1742 }
1743 return false;
1744 case KeyEvent.KEYCODE_FOCUS:
1745 if (mFirstTimeInitialized) {
1746 onShutterButtonFocus(false);
1747 }
Michael Kolb8872c232013-01-29 10:33:22 -08001748 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001749 }
1750 return false;
1751 }
1752
Michael Kolb8872c232013-01-29 10:33:22 -08001753 private void closeCamera() {
1754 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001755 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001756 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001757 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001758 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001759
Michael Kolb8872c232013-01-29 10:33:22 -08001760 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001761 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001762 mCameraDevice = null;
1763 setCameraState(PREVIEW_STOPPED);
1764 mFocusManager.onCameraReleased();
1765 }
1766 }
1767
1768 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001769 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1770 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001771 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001772 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001773 if (mFocusManager != null) {
1774 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1775 }
Doris Liu6432cd62013-06-13 17:20:31 -07001776 // Change the camera display orientation
1777 if (mCameraDevice != null) {
1778 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1779 }
Michael Kolb8872c232013-01-29 10:33:22 -08001780 }
1781
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001782 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001783 private void setupPreview() {
1784 mFocusManager.resetTouchFocus();
1785 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001786 }
1787
Angus Kong20fad242013-11-11 18:23:46 -08001788 /**
1789 * Returns whether we can/should start the preview or not.
1790 */
1791 private boolean checkPreviewPreconditions() {
1792 if (mPaused) {
1793 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001794 }
Michael Kolb8872c232013-01-29 10:33:22 -08001795
Angus Kong20fad242013-11-11 18:23:46 -08001796 if (mCameraDevice == null) {
1797 Log.w(TAG, "startPreview: camera device not ready yet.");
1798 return false;
1799 }
1800
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001801 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001802 if (st == null) {
1803 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001804 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001805 }
1806
1807 if (!mCameraPreviewParamsReady) {
1808 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001809 return false;
1810 }
1811 return true;
1812 }
1813
1814 /**
1815 * The start/stop preview should only run on the UI thread.
1816 */
1817 private void startPreview() {
1818 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001819 return;
1820 }
Angus Kong20fad242013-11-11 18:23:46 -08001821
Angus Kong2bca2102014-03-11 16:27:30 -07001822 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001823 setDisplayOrientation();
1824
1825 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001826 // If the focus mode is continuous autofocus, call cancelAutoFocus
1827 // to resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001828 String focusMode = mFocusManager.getFocusMode();
1829 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001830 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001831 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001832 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1833 }
1834 setCameraParameters(UPDATE_PARAM_ALL);
Doris Liucb8215e2014-05-19 11:53:32 -07001835
1836 // Workaround for KitKat and KitKat MR1 which leave configured preview
1837 // callback streams lingering around when they should have been removed.
1838 // These preview callback streams are the cause for distorted preview.
1839 // For more details, see b/12210027
Doris Liu6a8ae812014-05-20 12:43:56 -07001840 if (mShouldSetPreviewCallbacks) {
1841 mShouldSetPreviewCallbacks = false;
Doris Liucb8215e2014-05-19 11:53:32 -07001842 Size previewSize = new Size(mCameraDevice.getParameters().getPreviewSize());
1843 mCameraDevice.setPreviewDataCallbackWithBuffer(mHandler,
1844 new CameraManager.CameraPreviewDataCallback() {
1845 @Override
1846 public void onPreviewFrame(byte[] data, CameraProxy camera) {
1847 // Remove callback after the first frame comes in.
1848 mCameraDevice.setPreviewDataCallbackWithBuffer(null, null);
1849 }
1850 });
1851 mCameraDevice.addCallbackBuffer(new byte[previewSize.width() * previewSize.height()]);
1852 }
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001853 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001854
Alan Newbergerd41766f2014-04-09 18:25:34 -07001855 Log.i(TAG, "startPreview");
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001856 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001857
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001858 mFocusManager.onPreviewStarted();
1859 onPreviewStarted();
Andy Huibers10c58162014-03-29 14:06:54 -07001860 SessionStatsCollector.instance().previewActive(true);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001861 if (mSnapshotOnIdle) {
1862 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001863 }
1864 }
1865
Michael Kolbd6954f32013-03-08 20:43:01 -08001866 @Override
1867 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001868 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001869 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001870 mCameraDevice.stopPreview();
1871 mFaceDetectionStarted = false;
1872 }
1873 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001874 if (mFocusManager != null) {
1875 mFocusManager.onPreviewStopped();
1876 }
Andy Huibers10c58162014-03-29 14:06:54 -07001877 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001878 }
1879
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001880 @Override
Erin Dahlgren1648c362014-01-06 15:06:04 -08001881 public void onSettingChanged(SettingsManager settingsManager, int id) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001882 switch (id) {
1883 case SettingsManager.SETTING_FLASH_MODE: {
1884 updateParametersFlashMode();
1885 break;
1886 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001887 default: {
1888 // Do nothing.
1889 }
1890 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001891
1892 if (mCameraDevice != null) {
1893 mCameraDevice.setParameters(mParameters);
1894 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001895 }
1896
Michael Kolb8872c232013-01-29 10:33:22 -08001897 private void updateCameraParametersInitialize() {
1898 // Reset preview frame rate to the maximum because it may be lowered by
1899 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07001900 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07001901 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001902 mParameters.setPreviewFpsRange(
1903 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1904 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001905 }
1906
Angus Kongb50b5cb2013-08-09 14:55:20 -07001907 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001908
1909 // Disable video stabilization. Convenience methods not available in API
1910 // level <= 14
1911 String vstabSupported = mParameters.get("video-stabilization-supported");
1912 if ("true".equals(vstabSupported)) {
1913 mParameters.set("video-stabilization", "false");
1914 }
1915 }
1916
1917 private void updateCameraParametersZoom() {
1918 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07001919 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001920 mParameters.setZoom(mZoomValue);
1921 }
1922 }
1923
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001924 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001925 private void setAutoExposureLockIfSupported() {
1926 if (mAeLockSupported) {
1927 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1928 }
1929 }
1930
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001931 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001932 private void setAutoWhiteBalanceLockIfSupported() {
1933 if (mAwbLockSupported) {
1934 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1935 }
1936 }
1937
Michael Kolb8872c232013-01-29 10:33:22 -08001938 private void setFocusAreasIfSupported() {
1939 if (mFocusAreaSupported) {
1940 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1941 }
1942 }
1943
Michael Kolb8872c232013-01-29 10:33:22 -08001944 private void setMeteringAreasIfSupported() {
1945 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001946 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1947 }
1948 }
1949
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001950 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001951 setAutoExposureLockIfSupported();
1952 setAutoWhiteBalanceLockIfSupported();
1953 setFocusAreasIfSupported();
1954 setMeteringAreasIfSupported();
1955
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001956 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07001957 mFocusManager.overrideFocusMode(null);
1958 mParameters.setFocusMode(mFocusManager.getFocusMode());
Andy Huibers10c58162014-03-29 14:06:54 -07001959 SessionStatsCollector.instance().autofocusActive(
1960 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolbd3253f22013-07-12 11:36:47 -07001961
Michael Kolb8872c232013-01-29 10:33:22 -08001962 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001963 updateParametersPictureSize();
1964
1965 // Set JPEG quality.
1966 updateParametersPictureQuality();
1967
1968 // For the following settings, we need to check if the settings are
1969 // still supported by latest driver, if not, ignore the settings.
1970
1971 // Set exposure compensation
1972 updateParametersExposureCompensation();
1973
1974 // Set the scene mode: also sets flash and white balance.
1975 updateParametersSceneMode();
1976
1977 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1978 updateAutoFocusMoveCallback();
1979 }
1980 }
1981
1982 private void updateParametersPictureSize() {
1983 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001984 String pictureSize = settingsManager
1985 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
1986 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001987
Angus Kong63424662014-04-23 10:47:47 -07001988 List<Size> supported = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
Andy Huibers90c7ad52014-05-20 22:13:54 -07001989 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
1990 mCameraDevice.getCameraId(), supported);
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001991 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
1992 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07001993
1994 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
1995 mCameraDevice.getCameraId());
1996 if (ApiHelper.IS_NEXUS_5) {
1997 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
1998 mShouldResizeTo16x9 = true;
1999 } else {
2000 mShouldResizeTo16x9 = false;
2001 }
2002 }
Michael Kolb8872c232013-01-29 10:33:22 -08002003
2004 // Set a preview size that is closest to the viewfinder height and has
2005 // the right aspect ratio.
Angus Kong63424662014-04-23 10:47:47 -07002006 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPreviewSizes());
Angus Kongb50b5cb2013-08-09 14:55:20 -07002007 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002008 (double) size.width() / size.height());
Angus Kong63424662014-04-23 10:47:47 -07002009 Size original = new Size(mParameters.getPreviewSize());
Michael Kolb8872c232013-01-29 10:33:22 -08002010 if (!original.equals(optimalSize)) {
Doris Liu6a8ae812014-05-20 12:43:56 -07002011 if (ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK) {
2012 // Compare the aspect ratio.
2013 if ((original.width() * optimalSize.height())
2014 != (original.height() * optimalSize.width())) {
2015 // If aspect ratio has changed, set preview callback again, so
2016 // that the old preview callback stream will be forced to update.
2017 // This is a workaround for b/12210027, which was fixed in Kitkat MR2.
2018 mShouldSetPreviewCallbacks = true;
2019 }
2020 }
Angus Kong00b7b102014-04-24 15:46:52 -07002021 mParameters.setPreviewSize(optimalSize.width(), optimalSize.height());
Doris Liu6432cd62013-06-13 17:20:31 -07002022
Michael Kolb8872c232013-01-29 10:33:22 -08002023 // Zoom related settings will be changed for different preview
2024 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002025 if (mHandler.getLooper() == Looper.myLooper()) {
2026 // On UI thread only, not when camera starts up
2027 setupPreview();
2028 } else {
2029 mCameraDevice.setParameters(mParameters);
2030 }
Michael Kolb8872c232013-01-29 10:33:22 -08002031 mParameters = mCameraDevice.getParameters();
2032 }
Doris Liu95405742013-11-05 15:25:26 -08002033
Angus Kong00b7b102014-04-24 15:46:52 -07002034 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2035 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2036 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002037 }
Angus Kong00b7b102014-04-24 15:46:52 -07002038 Log.i(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002039 }
Michael Kolb8872c232013-01-29 10:33:22 -08002040
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002041 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002042 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2043 CameraProfile.QUALITY_HIGH);
2044 mParameters.setJpegQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002045 }
Michael Kolb8872c232013-01-29 10:33:22 -08002046
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002047 private void updateParametersExposureCompensation() {
2048 SettingsManager settingsManager = mActivity.getSettingsManager();
Spike Spragueabf54e22014-03-27 15:41:28 -07002049 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
2050 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
Angus Kong88289042014-04-22 16:39:42 -07002051 int max = mCameraCapabilities.getMaxExposureCompensation();
2052 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002053 if (value >= min && value <= max) {
2054 mParameters.setExposureCompensation(value);
2055 } else {
2056 Log.w(TAG, "invalid exposure range: " + value);
2057 }
Michael Kolb8872c232013-01-29 10:33:22 -08002058 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002059 // If exposure compensation is not enabled, reset the exposure compensation value.
2060 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002061 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002062
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002063 }
2064
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002065 private void updateParametersSceneMode() {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002066 SettingsManager settingsManager = mActivity.getSettingsManager();
2067
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002068 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
Angus Kong88289042014-04-22 16:39:42 -07002069 if (mCameraCapabilities
2070 .supports(mCameraCapabilities.getStringifier().sceneModeFromString(mSceneMode))) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002071 if (!mParameters.getSceneMode().equals(mSceneMode)) {
2072 mParameters.setSceneMode(mSceneMode);
2073
2074 // Setting scene mode will change the settings of flash mode,
2075 // white balance, and focus mode. Here we read back the
2076 // parameters, so we can know those settings.
2077 mCameraDevice.setParameters(mParameters);
2078 mParameters = mCameraDevice.getParameters();
2079 }
2080 } else {
2081 mSceneMode = mParameters.getSceneMode();
2082 if (mSceneMode == null) {
2083 mSceneMode = Parameters.SCENE_MODE_AUTO;
2084 }
2085 }
2086
Michael Kolb8872c232013-01-29 10:33:22 -08002087 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
2088 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002089 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002090
Michael Kolb8872c232013-01-29 10:33:22 -08002091 // Set focus mode.
2092 mFocusManager.overrideFocusMode(null);
2093 mParameters.setFocusMode(mFocusManager.getFocusMode());
2094 } else {
2095 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
2096 }
Michael Kolb8872c232013-01-29 10:33:22 -08002097 }
2098
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002099 private void updateParametersFlashMode() {
2100 SettingsManager settingsManager = mActivity.getSettingsManager();
2101
2102 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
Angus Kong88289042014-04-22 16:39:42 -07002103 if (mCameraCapabilities
2104 .supports(mCameraCapabilities.getStringifier().flashModeFromString(flashMode))) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002105 mParameters.setFlashMode(flashMode);
2106 }
2107 }
2108
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002109 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002110 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07002111 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07002112 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002113 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002114 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002115 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002116 }
2117 }
2118
Spike Spragueabf54e22014-03-27 15:41:28 -07002119 /**
2120 * Sets the exposure compensation to the given value and also updates settings.
2121 *
2122 * @param value exposure compensation value to be set
2123 */
2124 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002125 int max = mCameraCapabilities.getMaxExposureCompensation();
2126 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002127 if (value >= min && value <= max) {
2128 mParameters.setExposureCompensation(value);
2129 SettingsManager settingsManager = mActivity.getSettingsManager();
2130 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
2131 } else {
2132 Log.w(TAG, "invalid exposure range: " + value);
2133 }
2134 }
2135
Michael Kolb8872c232013-01-29 10:33:22 -08002136 // We separate the parameters into several subsets, so we can update only
2137 // the subsets actually need updating. The PREFERENCE set needs extra
2138 // locking because the preference can be changed from GLThread as well.
2139 private void setCameraParameters(int updateSet) {
2140 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2141 updateCameraParametersInitialize();
2142 }
2143
2144 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2145 updateCameraParametersZoom();
2146 }
2147
2148 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002149 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002150 }
2151
2152 mCameraDevice.setParameters(mParameters);
2153 }
2154
2155 // If the Camera is idle, update the parameters immediately, otherwise
2156 // accumulate them in mUpdateSet and update later.
2157 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2158 mUpdateSet |= additionalUpdateSet;
2159 if (mCameraDevice == null) {
2160 // We will update all the parameters when we open the device, so
2161 // we don't need to do anything now.
2162 mUpdateSet = 0;
2163 return;
2164 } else if (isCameraIdle()) {
2165 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002166 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002167 mUpdateSet = 0;
2168 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002169 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2170 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002171 }
2172 }
2173 }
2174
ztenghui7b265a62013-09-09 14:58:44 -07002175 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002176 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002177 return (mCameraState == IDLE) ||
2178 (mCameraState == PREVIEW_STOPPED) ||
2179 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002180 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002181 }
2182
ztenghui7b265a62013-09-09 14:58:44 -07002183 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002184 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002185 String action = mActivity.getIntent().getAction();
2186 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002187 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002188 }
2189
2190 private void setupCaptureParams() {
2191 Bundle myExtras = mActivity.getIntent().getExtras();
2192 if (myExtras != null) {
2193 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2194 mCropValue = myExtras.getString("crop");
2195 }
2196 }
2197
Michael Kolb8872c232013-01-29 10:33:22 -08002198 private void initializeCapabilities() {
2199 mInitialParams = mCameraDevice.getParameters();
Angus Kong88289042014-04-22 16:39:42 -07002200 mCameraCapabilities = mCameraDevice.getCapabilities();
2201 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2202 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2203 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2204 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kongdcccc512013-08-08 17:06:03 -07002205 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07002206 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002207 }
2208
Marco Nelissen0744e4a2013-11-22 01:47:37 +00002209 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08002210 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002211 public int onZoomChanged(int index) {
2212 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002213 if (mPaused) {
2214 return index;
2215 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002216 mZoomValue = index;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002217 if (mParameters == null || mCameraDevice == null) {
2218 return index;
2219 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002220 // Set zoom parameters asynchronously
2221 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07002222 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08002223 Parameters p = mCameraDevice.getParameters();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002224 if (p != null) {
2225 return p.getZoom();
2226 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002227 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002228 }
2229
2230 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002231 public int getCameraState() {
2232 return mCameraState;
2233 }
2234
2235 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002236 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002237 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002238 }
Angus Kong86d36312013-01-31 18:22:44 -08002239
2240 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002241 public void onLowMemory() {
2242 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002243 }
Angus Kong0d00a892013-03-26 11:40:40 -07002244
2245 @Override
2246 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2247 }
2248
2249 @Override
2250 public void onSensorChanged(SensorEvent event) {
2251 int type = event.sensor.getType();
2252 float[] data;
2253 if (type == Sensor.TYPE_ACCELEROMETER) {
2254 data = mGData;
2255 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2256 data = mMData;
2257 } else {
2258 // we should not be here.
2259 return;
2260 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002261 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002262 data[i] = event.values[i];
2263 }
2264 float[] orientation = new float[3];
2265 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2266 SensorManager.getOrientation(mR, orientation);
2267 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2268 if (mHeading < 0) {
2269 mHeading += 360;
2270 }
Angus Kong0d00a892013-03-26 11:40:40 -07002271 }
Doris Liu6432cd62013-06-13 17:20:31 -07002272
Ruben Brunkd217ed02013-10-08 23:31:13 -07002273 // For debugging only.
2274 public void setDebugUri(Uri uri) {
2275 mDebugUri = uri;
2276 }
2277
2278 // For debugging only.
2279 private void saveToDebugUri(byte[] data) {
2280 if (mDebugUri != null) {
2281 OutputStream outputStream = null;
2282 try {
2283 outputStream = mContentResolver.openOutputStream(mDebugUri);
2284 outputStream.write(data);
2285 outputStream.close();
2286 } catch (IOException e) {
2287 Log.e(TAG, "Exception while writing debug jpeg file", e);
2288 } finally {
2289 CameraUtil.closeSilently(outputStream);
2290 }
2291 }
2292 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002293
2294 @Override
2295 public void onRemoteShutterPress() {
2296 capture();
2297 }
Doris Liu6c751642014-05-05 18:43:26 -07002298
2299 /**
2300 * This class manages the loading/releasing/playing of the sounds needed for
2301 * countdown timer.
2302 */
2303 private class CountdownSoundPlayer {
2304 private SoundPool mSoundPool;
2305 private int mBeepOnce;
2306 private int mBeepTwice;
2307
2308 void loadSounds() {
2309 // Load the beeps.
2310 mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2311 mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
2312 mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
2313 }
2314
2315 void onRemainingSecondsChanged(int newVal) {
2316 if (mSoundPool == null) {
2317 Log.e(TAG, "Cannot play sound - they have not been loaded.");
2318 return;
2319 }
2320 if (newVal == 1) {
2321 mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
2322 } else if (newVal == 2 || newVal == 3) {
2323 mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
2324 }
2325 }
2326
2327 void release() {
2328 mSoundPool.release();
2329 mSoundPool = null;
2330 }
2331 }
Michael Kolb8872c232013-01-29 10:33:22 -08002332}