blob: ff52f99d72a7e8fd6a7fbcd882d55a23f98fbedf [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;
Angus Kong454d63f2014-05-06 14:45:59 -070074import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080075import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010076import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070077import com.android.camera.ui.CountDownView;
Angus Kongdcccc512013-08-08 17:06:03 -070078import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070079import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070080import com.android.camera.util.GcamHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070081import com.android.camera.util.SessionStatsCollector;
Angus Kong63424662014-04-23 10:47:47 -070082import com.android.camera.util.Size;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070083import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070084import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070085import com.android.camera2.R;
Seth Raphael5e09d012013-12-18 13:45:03 -080086import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080087
Angus Kong454d63f2014-05-06 14:45:59 -070088import java.io.ByteArrayOutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070089import java.io.File;
90import java.io.FileNotFoundException;
91import java.io.FileOutputStream;
92import java.io.IOException;
93import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010094import java.lang.ref.WeakReference;
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -070095import java.text.DecimalFormat;
Angus Kongdcccc512013-08-08 17:06:03 -070096import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070097import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -070098
Michael Kolb8872c232013-01-29 10:33:22 -080099public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800100 extends CameraModule
101 implements PhotoController,
102 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800103 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800104 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -0800105 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800106 SettingsManager.OnSettingChangedListener,
Doris Liu6c751642014-05-05 18:43:26 -0700107 RemoteCameraModule,
108 CountDownView.OnCountDownStatusListener {
Michael Kolb8872c232013-01-29 10:33:22 -0800109
Angus Kong2bca2102014-03-11 16:27:30 -0700110 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
Michael Kolb8872c232013-01-29 10:33:22 -0800111
112 // We number the request code from 1000 to avoid collision with Gallery.
113 private static final int REQUEST_CROP = 1000;
114
Angus Kong13e87c42013-11-25 10:02:47 -0800115 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800116 private static final int MSG_FIRST_TIME_INIT = 1;
117 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800118
119 // The subset of parameters we need to update in setCameraParameters().
120 private static final int UPDATE_PARAM_INITIALIZE = 1;
121 private static final int UPDATE_PARAM_ZOOM = 2;
122 private static final int UPDATE_PARAM_PREFERENCE = 4;
123 private static final int UPDATE_PARAM_ALL = -1;
124
Andy Huibersdef975d2013-11-22 09:13:39 -0800125 // This is the delay before we execute onResume tasks when coming
126 // from the lock screen, to allow time for onPause to execute.
127 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800128
Ruben Brunkd7488272013-10-10 18:45:53 -0700129 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
130
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700131 private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0");
132
Michael Kolb8872c232013-01-29 10:33:22 -0800133 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800134 private CameraProxy mCameraDevice;
135 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700136 private CameraCapabilities mCameraCapabilities;
Michael Kolb8872c232013-01-29 10:33:22 -0800137 private Parameters mParameters;
138 private boolean mPaused;
Doris Liucb8215e2014-05-19 11:53:32 -0700139 private boolean mPreviewFirstRunInCurrentModule = true;
Michael Kolbd6954f32013-03-08 20:43:01 -0800140
141 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800142
Michael Kolb8872c232013-01-29 10:33:22 -0800143 // The activity is going to switch to the specified camera id. This is
144 // needed because texture copy is done in GL thread. -1 means camera is not
145 // switching.
146 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800147
148 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
149 // needed to be updated in mUpdateSet.
150 private int mUpdateSet;
151
152 private static final int SCREEN_DELAY = 2 * 60 * 1000;
153
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800154 private int mZoomValue; // The current zoom value.
Michael Kolb8872c232013-01-29 10:33:22 -0800155
156 private Parameters mInitialParams;
157 private boolean mFocusAreaSupported;
158 private boolean mMeteringAreaSupported;
159 private boolean mAeLockSupported;
160 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700161 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800162
163 // The degrees of the device rotated clockwise from its natural orientation.
164 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800165
166 private static final String sTempCropFilename = "crop-temp";
167
Michael Kolb8872c232013-01-29 10:33:22 -0800168 private boolean mFaceDetectionStarted = false;
169
Michael Kolb8872c232013-01-29 10:33:22 -0800170 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
171 private String mCropValue;
172 private Uri mSaveUri;
173
Ruben Brunkd217ed02013-10-08 23:31:13 -0700174 private Uri mDebugUri;
175
Angus Kongce5480e2013-01-29 17:43:48 -0800176 // We use a queue to generated names of the images to be used later
177 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800178 private NamedImages mNamedImages;
179
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800180 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800181 @Override
182 public void run() {
183 onShutterButtonClick();
184 }
185 };
186
Michael Kolb8872c232013-01-29 10:33:22 -0800187 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800188 * An unpublished intent flag requesting to return as soon as capturing is
189 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800190 */
191 private static final String EXTRA_QUICK_CAPTURE =
192 "android.intent.extra.quickCapture";
193
194 // The display rotation in degrees. This is only valid when mCameraState is
195 // not PREVIEW_STOPPED.
196 private int mDisplayRotation;
197 // The value for android.hardware.Camera.setDisplayOrientation.
198 private int mCameraDisplayOrientation;
199 // The value for UI components like indicators.
200 private int mDisplayOrientation;
201 // The value for android.hardware.Camera.Parameters.setRotation.
202 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700203 // Indicates whether we are using front camera
204 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800205 private boolean mFirstTimeInitialized;
206 private boolean mIsImageCaptureIntent;
207
Michael Kolb8872c232013-01-29 10:33:22 -0800208 private int mCameraState = PREVIEW_STOPPED;
209 private boolean mSnapshotOnIdle = false;
210
211 private ContentResolver mContentResolver;
212
213 private LocationManager mLocationManager;
Doris Liu2b906b82013-12-10 16:34:08 -0800214 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800215
Michael Kolb8872c232013-01-29 10:33:22 -0800216 private final PostViewPictureCallback mPostViewPictureCallback =
217 new PostViewPictureCallback();
218 private final RawPictureCallback mRawPictureCallback =
219 new RawPictureCallback();
220 private final AutoFocusCallback mAutoFocusCallback =
221 new AutoFocusCallback();
222 private final Object mAutoFocusMoveCallback =
223 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700224 ? new AutoFocusMoveCallback()
225 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800226
227 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
228
229 private long mFocusStartTime;
230 private long mShutterCallbackTime;
231 private long mPostViewPictureCallbackTime;
232 private long mRawPictureCallbackTime;
233 private long mJpegPictureCallbackTime;
234 private long mOnResumeTime;
235 private byte[] mJpegImageData;
236
237 // These latency time are for the CameraLatency test.
238 public long mAutoFocusTime;
239 public long mShutterLag;
240 public long mShutterToPictureDisplayedTime;
241 public long mPictureDisplayedToJpegCallbackTime;
242 public long mJpegCallbackFinishTime;
243 public long mCaptureStartTime;
244
245 // This handles everything about focus.
246 private FocusOverlayManager mFocusManager;
247
Doris Liubd1b8f92014-01-03 17:59:51 -0800248 private final int mGcamModeIndex;
Doris Liu6c751642014-05-05 18:43:26 -0700249 private final CountdownSoundPlayer mCountdownSoundPlayer = new CountdownSoundPlayer();
Doris Liubd1b8f92014-01-03 17:59:51 -0800250
Michael Kolb8872c232013-01-29 10:33:22 -0800251 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800252
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100253 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800254
Michael Kolb8872c232013-01-29 10:33:22 -0800255 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700256 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800257 private final float[] mGData = new float[3];
258 private final float[] mMData = new float[3];
259 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700260 private int mHeading = -1;
261
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800262 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700263 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700264
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800265 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800266 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800267 @Override
268 public void onMediaSaved(Uri uri) {
269 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700270 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800271 }
272 }
273 };
Angus Kong454d63f2014-05-06 14:45:59 -0700274 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800275
Angus Kong0b9eb5b2014-04-30 15:03:33 -0700276 private final Runnable mResumeTaskRunnable = new Runnable() {
277 @Override
278 public void run() {
279 onResumeTasks();
280 }
281 };
282
Doris Liudb8f9752014-05-12 15:25:13 -0700283 /**
284 * This callback gets called when user select whether or not to
285 * turn on geo-tagging.
286 */
287 public interface LocationDialogCallback {
288 /**
289 * Gets called after user selected/unselected geo-tagging feature.
290 *
291 * @param selected whether or not geo-tagging feature is selected
292 */
293 public void onLocationTaggingSelected(boolean selected);
294 }
295
296 /**
297 * This callback defines the text that is shown in the aspect ratio selection
298 * dialog, provides the current aspect ratio, and gets notified when user changes
299 * aspect ratio selection in the dialog.
300 */
301 public interface AspectRatioDialogCallback {
302 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700303 * Returns current aspect ratio that is being used to set as default.
304 */
305 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
306
307 /**
308 * Gets notified when user has made the aspect ratio selection.
309 *
310 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700311 * @param dialogHandlingFinishedRunnable runnable to run when the operations
312 * needed to handle changes from dialog
313 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700314 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700315 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
316 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700317 }
318
Angus Kongdcccc512013-08-08 17:06:03 -0700319 private void checkDisplayRotation() {
320 // Set the display orientation if display rotation has changed.
321 // Sometimes this happens when the device is held upside
322 // down and camera app is opened. Rotation animation will
323 // take some time and the rotation value we have got may be
324 // wrong. Framework does not have a callback for this now.
325 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
326 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800327 }
Angus Kongdcccc512013-08-08 17:06:03 -0700328 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
329 mHandler.postDelayed(new Runnable() {
330 @Override
331 public void run() {
332 checkDisplayRotation();
333 }
334 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800335 }
336 }
337
338 /**
339 * This Handler is used to post message back onto the main thread of the
340 * application
341 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100342 private static class MainHandler extends Handler {
343 private final WeakReference<PhotoModule> mModule;
344
345 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800346 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100347 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800348 }
349
Michael Kolb8872c232013-01-29 10:33:22 -0800350 @Override
351 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100352 PhotoModule module = mModule.get();
353 if (module == null) {
354 return;
355 }
Michael Kolb8872c232013-01-29 10:33:22 -0800356 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800357 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100358 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800359 break;
360 }
361
Angus Kong13e87c42013-11-25 10:02:47 -0800362 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100363 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800364 break;
365 }
Michael Kolb8872c232013-01-29 10:33:22 -0800366 }
367 }
368 }
369
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700370 private void switchToGcamCapture() {
371 if (mActivity != null && mGcamModeIndex != 0) {
372 SettingsManager settingsManager = mActivity.getSettingsManager();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700373 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700374 SettingsManager.VALUE_ON);
375
376 // Disable the HDR+ button to prevent callbacks from being
377 // queued before the correct callback is attached to the button
378 // in the new module. The new module will set the enabled/disabled
379 // of this button when the module's preferred camera becomes available.
380 ButtonManager buttonManager = mActivity.getButtonManager();
381 buttonManager.disableButton(ButtonManager.BUTTON_HDRPLUS);
382
383 // Do not post this to avoid this module switch getting interleaved with
384 // other button callbacks.
385 mActivity.onModeSelected(mGcamModeIndex);
386 }
387 }
388
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800389 /**
390 * Constructs a new photo module.
391 */
Angus Kongc4e66562013-11-22 23:03:21 -0800392 public PhotoModule(AppController app) {
393 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800394 mGcamModeIndex = app.getAndroidContext().getResources()
395 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800396 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700397
Michael Kolb8872c232013-01-29 10:33:22 -0800398 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100399 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
400 mActivity = activity;
401 // TODO: Need to look at the controller interface to see if we can get
402 // rid of passing in the activity directly.
403 mAppController = mActivity;
404
405 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
406 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800407
408 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800409 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800410
Doris Liudb8f9752014-05-12 15:25:13 -0700411 // TODO: Move this to SettingsManager as a part of upgrade procedure.
412 if (!settingsManager.getBoolean(SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO)) {
413 // Switch to back camera to set aspect ratio.
414 mCameraId = Integer.parseInt(settingsManager
415 .getDefaultCameraIdSetting(activity).getDefault());
416 }
417
Michael Kolb8872c232013-01-29 10:33:22 -0800418 mContentResolver = mActivity.getContentResolver();
419
Michael Kolb8872c232013-01-29 10:33:22 -0800420 // Surface texture is from camera screen nail and startPreview needs it.
421 // This must be done before startPreview.
422 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800423
Angus Kong20fad242013-11-11 18:23:46 -0800424 mActivity.getCameraProvider().requestCamera(mCameraId);
425
Michael Kolb8872c232013-01-29 10:33:22 -0800426 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800427 mLocationManager = mActivity.getLocationManager();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800428 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700429 mUI.setCountdownFinishedListener(this);
430
431 // TODO: Make this a part of app controller API.
432 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
433 cancelButton.setOnClickListener(new View.OnClickListener() {
434 @Override
435 public void onClick(View view) {
436 cancelCountDown();
437 }
438 });
439 }
440
441 private void cancelCountDown() {
442 if (mUI.isCountingDown()) {
443 // Cancel on-going countdown.
444 mUI.cancelCountDown();
445 }
446 mAppController.getCameraAppUI().transitionToCapture();
447 mAppController.getCameraAppUI().showModeOptions();
Michael Kolbd6954f32013-03-08 20:43:01 -0800448 }
449
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800450 @Override
451 public boolean isUsingBottomBar() {
452 return true;
453 }
454
Michael Kolbd6954f32013-03-08 20:43:01 -0800455 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800456 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800457 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800458 setupCaptureParams();
459 }
460 }
461
462 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800463 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800464 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800465 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700466 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800467 }
468
Doris Liudb8f9752014-05-12 15:25:13 -0700469 /**
470 * Prompt the user to pick to record location and choose aspect ratio for the
471 * very first run of camera only.
472 */
473 private void settingsFirstRun() {
474 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100475
Doris Liudb8f9752014-05-12 15:25:13 -0700476 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800477 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800478 }
Doris Liudb8f9752014-05-12 15:25:13 -0700479
480 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION);
481 boolean aspectRatioPrompt = !settingsManager.getBoolean(
482 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO);
483 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800484 return;
485 }
Doris Liudb8f9752014-05-12 15:25:13 -0700486
Michael Kolb8872c232013-01-29 10:33:22 -0800487 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800488 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800489 if (backCameraId == -1) {
490 // If there is no back camera, do not show the prompt.
491 return;
492 }
Doris Liudb8f9752014-05-12 15:25:13 -0700493
494 if (locationPrompt) {
495 // Show both location and aspect ratio selection dialog.
496 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700497 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700498 public void onLocationTaggingSelected(boolean selected) {
499 settingsManager.setLocation(selected, mActivity.getLocationManager());
500 }
501 }, createAspectRatioDialogCallback());
502 } else {
503 // App upgrade. Only show aspect ratio selection.
504 mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
505 }
506 }
507
508 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
509 Size currentSize = new Size(mParameters.getPictureSize());
510 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
511 if (aspectRatio < 1f) {
512 aspectRatio = 1 / aspectRatio;
513 }
514 final AspectRatioSelector.AspectRatio currentAspectRatio;
515 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
516 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
517 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
518 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
519 } else {
520 // TODO: Log error and not show dialog.
521 return null;
522 }
523
524 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
525 List<Size> pictureSizes = ResolutionUtil
526 .getDisplayableSizesFromSupported(sizes, true);
527
528 // This logic below finds the largest resolution for each aspect ratio.
529 // TODO: Move this somewhere that can be shared with SettingsActivity
530 int aspectRatio4x3Resolution = 0;
531 int aspectRatio16x9Resolution = 0;
532 Size largestSize4x3 = new Size(0, 0);
533 Size largestSize16x9 = new Size(0, 0);
534 for (Size size : pictureSizes) {
535 float pictureAspectRatio = (float) size.width() / (float) size.height();
536 pictureAspectRatio = pictureAspectRatio < 1 ?
537 1f / pictureAspectRatio : pictureAspectRatio;
538 int resolution = size.width() * size.height();
539 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
540 if (resolution > aspectRatio4x3Resolution) {
541 aspectRatio4x3Resolution = resolution;
542 largestSize4x3 = size;
543 }
544 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
545 if (resolution > aspectRatio16x9Resolution) {
546 aspectRatio16x9Resolution = resolution;
547 largestSize16x9 = size;
548 }
549 }
550 }
Doris Liudb8f9752014-05-12 15:25:13 -0700551
Doris Liu08b0cdd2014-05-13 19:19:55 -0700552 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
553 final Size size4x3ToSelect = largestSize4x3;
554 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700555
Doris Liudb8f9752014-05-12 15:25:13 -0700556 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700557
558 @Override
559 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
560 return currentAspectRatio;
561 }
562
563 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700564 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
565 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700566 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700567 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700568 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
569 largestSize4x3Text);
570 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700571 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700572 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
573 largestSize16x9Text);
574 }
575 mActivity.getSettingsManager().setBoolean(
576 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO, true);
577 if (newAspectRatio != currentAspectRatio) {
Doris Liudb8f9752014-05-12 15:25:13 -0700578 stopPreview();
579 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700580 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
581 } else {
582 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700583 }
584 }
585 };
586 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700587 }
Michael Kolb8872c232013-01-29 10:33:22 -0800588
ztenghui7b265a62013-09-09 14:58:44 -0700589 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700590 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800591 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700592 }
593
594 @Override
595 public void onPreviewUIDestroyed() {
596 if (mCameraDevice == null) {
597 return;
598 }
599 mCameraDevice.setPreviewTexture(null);
600 stopPreview();
601 }
602
Doris Liu1dfe7822013-12-12 00:02:08 -0800603 @Override
604 public void startPreCaptureAnimation() {
605 mAppController.startPreCaptureAnimation();
606 }
607
Michael Kolbd6954f32013-03-08 20:43:01 -0800608 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800609 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800610 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800611 }
612
Michael Kolbd6954f32013-03-08 20:43:01 -0800613 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800614 if (mPaused) {
615 return;
616 }
Doris Liu6c751642014-05-05 18:43:26 -0700617 cancelCountDown();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800618 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800619
Alan Newbergerd41766f2014-04-09 18:25:34 -0700620 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800621 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800622 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800623 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800624 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800625 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800626 if (mFocusManager != null) {
627 mFocusManager.removeMessages();
628 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800629
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700630 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700631 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700632 // Start switch camera animation. Post a message because
633 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800634 }
635
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800636 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100637 new ButtonManager.ButtonCallback() {
638 @Override
639 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800640 // At the time this callback is fired, the camera id
641 // has be set to the desired camera.
642
Angus Kong97e282a2014-03-04 18:44:49 -0800643 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100644 return;
645 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800646 // If switching to back camera, and HDR+ is still on,
647 // switch back to gcam, otherwise handle callback normally.
648 SettingsManager settingsManager = mActivity.getSettingsManager();
649 if (settingsManager.isCameraBackFacing()) {
650 if (settingsManager.requestsReturnToHdrPlus()) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700651 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800652 return;
653 }
654 }
655
Sascha Haeberlingde303232014-02-07 02:30:53 +0100656 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800657
Alan Newbergerd41766f2014-04-09 18:25:34 -0700658 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100659 // We need to keep a preview frame for the animation before
660 // releasing the camera. This will trigger
661 // onPreviewTextureCopied.
662 // TODO: Need to animate the camera switch
663 switchCamera();
664 }
665 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800666
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800667 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100668 new ButtonManager.ButtonCallback() {
669 @Override
670 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700671 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100672 if (GcamHelper.hasGcamCapture()) {
673 // Set the camera setting to default backfacing.
Sascha Haeberlingde303232014-02-07 02:30:53 +0100674 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700675 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100676 } else {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700677 if (settingsManager.isHdrOn()) {
678 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
679 CameraUtil.SCENE_MODE_HDR);
680 } else {
681 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
682 Parameters.SCENE_MODE_AUTO);
683 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100684 updateParametersSceneMode();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700685 mCameraDevice.setParameters(mParameters);
686 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100687 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800688 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100689 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800690
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800691 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
692 @Override
693 public void onClick(View v) {
694 onCaptureCancelled();
695 }
696 };
697
698 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
699 @Override
700 public void onClick(View v) {
701 onCaptureDone();
702 }
703 };
704
705 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
706 @Override
707 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800708 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800709 onCaptureRetake();
710 }
711 };
712
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800713 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700714 public void hardResetSettings(SettingsManager settingsManager) {
715 // PhotoModule should hard reset HDR+ to off.
716 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
717 }
718
719 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800720 public HardwareSpec getHardwareSpec() {
Erin Dahlgren49ab9222014-01-28 17:40:28 -0800721 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800722 }
723
724 @Override
725 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
726 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
727
728 bottomBarSpec.enableCamera = true;
729 bottomBarSpec.cameraCallback = mCameraCallback;
730 bottomBarSpec.enableFlash = true;
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800731 bottomBarSpec.enableHdr = true;
732 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800733 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700734 if (mCameraCapabilities != null) {
735 bottomBarSpec.enableExposureCompensation = true;
736 bottomBarSpec.exposureCompensationSetCallback =
737 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
738 @Override
739 public void setExposure(int value) {
740 setExposureCompensation(value);
741 }
742 };
743 bottomBarSpec.minExposureCompensation =
744 mCameraCapabilities.getMinExposureCompensation();
745 bottomBarSpec.maxExposureCompensation =
746 mCameraCapabilities.getMaxExposureCompensation();
747 bottomBarSpec.exposureCompensationStep =
748 mCameraCapabilities.getExposureCompensationStep();
749 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800750
Doris Liu6c751642014-05-05 18:43:26 -0700751 bottomBarSpec.enableSelfTimer = true;
752
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800753 if (isImageCaptureIntent()) {
754 bottomBarSpec.showCancel = true;
755 bottomBarSpec.cancelCallback = mCancelCallback;
756 bottomBarSpec.showDone = true;
757 bottomBarSpec.doneCallback = mDoneCallback;
758 bottomBarSpec.showRetake = true;
759 bottomBarSpec.retakeCallback = mRetakeCallback;
760 }
761
762 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800763 }
764
Michael Kolbd6954f32013-03-08 20:43:01 -0800765 // either open a new camera or switch cameras
766 private void openCameraCommon() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800767 mUI.onCameraOpened(mParameters);
Angus Kong0fb819b2013-10-08 13:44:19 -0700768 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800769 // Set hdr plus to default: off.
770 SettingsManager settingsManager = mActivity.getSettingsManager();
771 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700772 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800773 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800774 }
775
ztenghui7b265a62013-09-09 14:58:44 -0700776 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800777 public void updatePreviewAspectRatio(float aspectRatio) {
778 mAppController.updatePreviewAspectRatio(aspectRatio);
779 }
780
Michael Kolb8872c232013-01-29 10:33:22 -0800781 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800782 SettingsManager settingsManager = mActivity.getSettingsManager();
783 if (settingsManager == null) {
784 Log.e(TAG, "Settings manager is null!");
785 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800786 }
Spike Spragueabf54e22014-03-27 15:41:28 -0700787 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
Michael Kolb8872c232013-01-29 10:33:22 -0800788 }
789
Michael Kolb8872c232013-01-29 10:33:22 -0800790 // Snapshots can only be taken after this is called. It should be called
791 // once only. We could have done these things in onCreate() but we want to
792 // make preview screen appear as soon as possible.
793 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700794 if (mFirstTimeInitialized || mPaused) {
795 return;
796 }
Michael Kolb8872c232013-01-29 10:33:22 -0800797
Michael Kolbd6954f32013-03-08 20:43:01 -0800798 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800799
Angus Kong86d36312013-01-31 18:22:44 -0800800 // We set the listener only when both service and shutterbutton
801 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800802 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800803
Michael Kolb8872c232013-01-29 10:33:22 -0800804 mNamedImages = new NamedImages();
805
806 mFirstTimeInitialized = true;
807 addIdleHandler();
808
Spike Spraguee6374b72014-04-25 17:24:32 -0700809 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800810 }
811
Michael Kolbd6954f32013-03-08 20:43:01 -0800812 // If the activity is paused and resumed, this method will be called in
813 // onResume.
814 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800815 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800816 mNamedImages = new NamedImages();
817 mUI.initializeSecondTime(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -0800818 }
819
Michael Kolb8872c232013-01-29 10:33:22 -0800820 private void addIdleHandler() {
821 MessageQueue queue = Looper.myQueue();
822 queue.addIdleHandler(new MessageQueue.IdleHandler() {
823 @Override
824 public boolean queueIdle() {
825 Storage.ensureOSXCompatible();
826 return false;
827 }
828 });
829 }
830
Michael Kolb8872c232013-01-29 10:33:22 -0800831 @Override
832 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800833 if (mFaceDetectionStarted) {
834 return;
835 }
Angus Kong88289042014-04-22 16:39:42 -0700836 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800837 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700838 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700839 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800840 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700841 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800842 }
843 }
844
Michael Kolb8872c232013-01-29 10:33:22 -0800845 @Override
846 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800847 if (!mFaceDetectionStarted) {
848 return;
849 }
Angus Kong88289042014-04-22 16:39:42 -0700850 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800851 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700852 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800853 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800854 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700855 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800856 }
857 }
858
Michael Kolb8872c232013-01-29 10:33:22 -0800859 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700860 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700861
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800862 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700863
Sascha Haeberling37f36112013-08-06 14:31:52 -0700864 public ShutterCallback(boolean needsAnimation) {
865 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700866 }
867
Michael Kolb8872c232013-01-29 10:33:22 -0800868 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700869 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800870 mShutterCallbackTime = System.currentTimeMillis();
871 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
872 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700873 if (mNeedsAnimation) {
874 mActivity.runOnUiThread(new Runnable() {
875 @Override
876 public void run() {
877 animateAfterShutter();
878 }
879 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700880 }
Michael Kolb8872c232013-01-29 10:33:22 -0800881 }
882 }
883
Angus Kong9ef99252013-07-18 18:04:19 -0700884 private final class PostViewPictureCallback
885 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800886 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800887 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800888 mPostViewPictureCallbackTime = System.currentTimeMillis();
889 Log.v(TAG, "mShutterToPostViewCallbackTime = "
890 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
891 + "ms");
892 }
893 }
894
Angus Kong9ef99252013-07-18 18:04:19 -0700895 private final class RawPictureCallback
896 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800897 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800898 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800899 mRawPictureCallbackTime = System.currentTimeMillis();
900 Log.v(TAG, "mShutterToRawCallbackTime = "
901 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
902 }
903 }
904
Angus Kong454d63f2014-05-06 14:45:59 -0700905 private static class ResizeBundle {
906 byte[] jpegData;
907 float targetAspectRatio;
908 ExifInterface exif;
909 }
910
911 /**
912 * @return Cropped image if the target aspect ratio is larger than the jpeg
913 * aspect ratio on the long axis. The original jpeg otherwise.
914 */
915 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
916
917 final byte[] jpegData = dataBundle.jpegData;
918 final ExifInterface exif = dataBundle.exif;
919 float targetAspectRatio = dataBundle.targetAspectRatio;
920
921 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
922 int originalWidth = original.getWidth();
923 int originalHeight = original.getHeight();
924 int newWidth;
925 int newHeight;
926
927 if (originalWidth > originalHeight) {
928 newHeight = (int) (originalWidth / targetAspectRatio);
929 newWidth = originalWidth;
930 } else {
931 newWidth = (int) (originalHeight / targetAspectRatio);
932 newHeight = originalHeight;
933 }
934 int xOffset = (originalWidth - newWidth)/2;
935 int yOffset = (originalHeight - newHeight)/2;
936
937 if (xOffset < 0 || yOffset < 0) {
938 return dataBundle;
939 }
940
941 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
942 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
943 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
944
945 ByteArrayOutputStream stream = new ByteArrayOutputStream();
946
947 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
948 dataBundle.jpegData = stream.toByteArray();
949 return dataBundle;
950 }
951
Angus Kong9ef99252013-07-18 18:04:19 -0700952 private final class JpegPictureCallback
953 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800954 Location mLocation;
955
956 public JpegPictureCallback(Location loc) {
957 mLocation = loc;
958 }
959
960 @Override
Angus Kong454d63f2014-05-06 14:45:59 -0700961 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700962 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800963 if (mPaused) {
964 return;
965 }
Doris Liu6432cd62013-06-13 17:20:31 -0700966 if (mIsImageCaptureIntent) {
967 stopPreview();
968 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700969 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700970 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800971 }
972
973 mJpegPictureCallbackTime = System.currentTimeMillis();
974 // If postview callback has arrived, the captured image is displayed
975 // in postview callback. If not, the captured image is displayed in
976 // raw picture callback.
977 if (mPostViewPictureCallbackTime != 0) {
978 mShutterToPictureDisplayedTime =
979 mPostViewPictureCallbackTime - mShutterCallbackTime;
980 mPictureDisplayedToJpegCallbackTime =
981 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
982 } else {
983 mShutterToPictureDisplayedTime =
984 mRawPictureCallbackTime - mShutterCallbackTime;
985 mPictureDisplayedToJpegCallbackTime =
986 mJpegPictureCallbackTime - mRawPictureCallbackTime;
987 }
988 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
989 + mPictureDisplayedToJpegCallbackTime + "ms");
990
Michael Kolb8872c232013-01-29 10:33:22 -0800991 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
992 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700993 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800994 }
995
Angus Kong454d63f2014-05-06 14:45:59 -0700996 long now = System.currentTimeMillis();
997 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
998 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
999 mJpegPictureCallbackTime = 0;
1000
1001 final ExifInterface exif = Exif.getExif(originalJpegData);
1002
1003 if (mShouldResizeTo16x9) {
1004 final ResizeBundle dataBundle = new ResizeBundle();
1005 dataBundle.jpegData = originalJpegData;
1006 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1007 dataBundle.exif = exif;
1008 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1009
1010 @Override
1011 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1012 return cropJpegDataToAspectRatio(resizeBundles[0]);
1013 }
1014
1015 @Override
1016 protected void onPostExecute(ResizeBundle result) {
1017 saveFinalPhoto(result.jpegData, result.exif, camera);
1018 }
1019 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1020
1021 } else {
1022 saveFinalPhoto(originalJpegData, exif, camera);
1023 }
1024 }
1025
1026 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1027
Doris Liu36e56fb2013-09-11 17:38:08 -07001028 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001029
Andy Huiberse08bc042014-04-11 19:26:47 -07001030 float zoomValue = 0f;
Angus Kong88289042014-04-22 16:39:42 -07001031 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Andy Huiberse08bc042014-04-11 19:26:47 -07001032 int zoomIndex = mParameters.getZoom();
1033 List<Integer> zoomRatios = mParameters.getZoomRatios();
1034 if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
1035 zoomValue = 0.01f * zoomRatios.get(zoomIndex);
1036 }
1037 }
Angus Kong454d63f2014-05-06 14:45:59 -07001038
Andy Huibers6b9743a2014-04-03 23:23:29 -07001039 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
Andy Huibers203abe52014-05-19 13:59:01 -07001040 String flashSetting =
1041 mActivity.getSettingsManager().get(SettingsManager.SETTING_FLASH_MODE);
1042 boolean gridLinesOn = mActivity.getSettingsManager().areGridLinesOn();
Andy Huibers10c58162014-03-29 14:06:54 -07001043 UsageStatistics.instance().photoCaptureDoneEvent(
1044 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1045 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers203abe52014-05-19 13:59:01 -07001046 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn);
Ruben Brunkd217ed02013-10-08 23:31:13 -07001047
Ruben Brunkd7488272013-10-10 18:45:53 -07001048 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001049 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001050 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1051 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001052 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001053 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1054 width = exifWidth;
1055 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001056 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001057 Size s;
1058 s = new Size(mParameters.getPictureSize());
1059 if ((mJpegRotation + orientation) % 180 == 0) {
1060 width = s.width();
1061 height = s.height();
1062 } else {
1063 width = s.height();
1064 height = s.width();
1065 }
Michael Kolb8872c232013-01-29 10:33:22 -08001066 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001067 NamedEntity name = mNamedImages.getNextNameEntity();
1068 String title = (name == null) ? null : name.title;
1069 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001070
1071 // Handle debug mode outputs
1072 if (mDebugUri != null) {
1073 // If using a debug uri, save jpeg there.
1074 saveToDebugUri(jpegData);
1075
1076 // Adjust the title of the debug image shown in mediastore.
1077 if (title != null) {
1078 title = DEBUG_IMAGE_PREFIX + title;
1079 }
1080 }
1081
Michael Kolb8872c232013-01-29 10:33:22 -08001082 if (title == null) {
1083 Log.e(TAG, "Unbalanced name/data pair");
1084 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001085 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001086 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001087 }
Angus Kong0d00a892013-03-26 11:40:40 -07001088 if (mHeading >= 0) {
1089 // heading direction has been updated by the sensor.
1090 ExifTag directionRefTag = exif.buildTag(
1091 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1092 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1093 ExifTag directionTag = exif.buildTag(
1094 ExifInterface.TAG_GPS_IMG_DIRECTION,
1095 new Rational(mHeading, 1));
1096 exif.setTag(directionRefTag);
1097 exif.setTag(directionTag);
1098 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001099 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001100 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001101 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001102 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001103 // Animate capture with real jpeg data instead of a preview
1104 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001105 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001106 } else {
1107 mJpegImageData = jpegData;
1108 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001109 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001110 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001111 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001112 }
1113 }
1114
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001115 // Send the taken photo to remote shutter listeners, if any are
1116 // registered.
1117 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
1118 @Override
1119 public void run() {
1120 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1121 }
1122 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001123
Michael Kolb8872c232013-01-29 10:33:22 -08001124 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001125 // latency. It's true that someone else could write to the SD card
1126 // in the mean time and fill it, but that could have happened
1127 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001128 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001129 }
1130 }
1131
Angus Kong9ef99252013-07-18 18:04:19 -07001132 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001133 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001134 public void onAutoFocus(boolean focused, CameraProxy camera) {
1135 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001136 if (mPaused) {
1137 return;
1138 }
Michael Kolb8872c232013-01-29 10:33:22 -08001139
1140 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001141 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001142 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001143 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001144 }
1145 }
1146
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001147 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001148 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001149 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001150 @Override
1151 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001152 boolean moving, CameraProxy camera) {
1153 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001154 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001155 }
1156 }
1157
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001158 /**
1159 * This class is just a thread-safe queue for name,date holder objects.
1160 */
1161 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001162 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001163
1164 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001165 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001166 }
1167
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001168 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001169 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001170 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001171 r.date = date;
1172 mQueue.add(r);
1173 }
1174
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001175 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001176 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001177 if (!mQueue.isEmpty()) {
1178 return mQueue.remove(0);
1179 }
Michael Kolb8872c232013-01-29 10:33:22 -08001180 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001181 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001182 }
1183
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001184 public static class NamedEntity {
1185 public String title;
1186 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001187 }
1188 }
1189
1190 private void setCameraState(int state) {
1191 mCameraState = state;
1192 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001193 case PREVIEW_STOPPED:
1194 case SNAPSHOT_IN_PROGRESS:
1195 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001196 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001197 break;
1198 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001199 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001200 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001201 }
1202 }
1203
Sascha Haeberling37f36112013-08-06 14:31:52 -07001204 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001205 // Only animate when in full screen capture mode
1206 // i.e. If monkey/a user swipes to the gallery during picture taking,
1207 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001208 if (!mIsImageCaptureIntent) {
1209 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001210 }
Michael Kolb8872c232013-01-29 10:33:22 -08001211 }
1212
1213 @Override
1214 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001215 // If we are already in the middle of taking a snapshot or the image
1216 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001217 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001218 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001219 return false;
1220 }
1221 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001222
Michael Kolb8872c232013-01-29 10:33:22 -08001223 mPostViewPictureCallbackTime = 0;
1224 mJpegImageData = null;
1225
Angus Kongb50b5cb2013-08-09 14:55:20 -07001226 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001227
1228 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001229 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001230 }
1231
1232 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -08001233 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001234
Doris Liu3cf565c2013-02-15 10:55:37 -08001235 // We need to be consistent with the framework orientation (i.e. the
1236 // orientation of the UI.) when the auto-rotate screen setting is on.
1237 if (mActivity.isAutoRotateScreen()) {
1238 orientation = (360 - mDisplayRotation) % 360;
1239 } else {
1240 orientation = mOrientation;
1241 }
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +01001242 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
1243 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001244 mParameters.setRotation(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001245 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001246 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -08001247 mCameraDevice.setParameters(mParameters);
1248
Sascha Haeberling88901942013-08-28 17:49:00 -07001249 // We don't want user to press the button again while taking a
1250 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001251 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001252 mCameraDevice.takePicture(mHandler,
1253 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001254 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001255 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001256
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001257 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001258
1259 mFaceDetectionStarted = false;
1260 setCameraState(SNAPSHOT_IN_PROGRESS);
1261 return true;
1262 }
1263
1264 @Override
1265 public void setFocusParameters() {
1266 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1267 }
1268
Michael Kolbd6954f32013-03-08 20:43:01 -08001269 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001270 // If scene mode is set, we cannot set flash mode, white balance, and
1271 // focus mode, instead, we read it from driver
1272 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001273 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001274 }
1275 }
1276
Doris Liu8ad8ad42014-03-27 14:43:31 -07001277 private void overrideCameraSettings(final String flashMode, final String focusMode) {
Erin Dahlgrene419b192013-12-03 13:10:27 -08001278 SettingsManager settingsManager = mActivity.getSettingsManager();
1279 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
Erin Dahlgrene419b192013-12-03 13:10:27 -08001280 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001281 }
1282
1283 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001284 public void onOrientationChanged(int orientation) {
1285 // We keep the last known orientation. So if the user first orient
1286 // the camera then point the camera to floor or sky, we still have
1287 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001288 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1289 return;
1290 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001291 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001292 }
1293
1294 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001295 public void onCameraAvailable(CameraProxy cameraProxy) {
1296 if (mPaused) {
1297 return;
1298 }
1299 mCameraDevice = cameraProxy;
1300
1301 initializeCapabilities();
1302
1303 // Reset zoom value index.
1304 mZoomValue = 0;
1305 if (mFocusManager == null) {
1306 initializeFocusManager();
1307 }
Angus Kong88289042014-04-22 16:39:42 -07001308 mFocusManager.setParameters(mInitialParams, mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001309
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001310 // Do camera parameter dependent initialization.
Angus Kong20fad242013-11-11 18:23:46 -08001311 mParameters = mCameraDevice.getParameters();
1312 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001313 // Set a listener which updates camera parameters based
1314 // on changed settings.
1315 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001316 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001317 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001318
Angus Kong20fad242013-11-11 18:23:46 -08001319 startPreview();
1320
1321 onCameraOpened();
1322 }
1323
1324 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001325 public void onCaptureCancelled() {
1326 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1327 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001328 }
1329
Michael Kolbd6954f32013-03-08 20:43:01 -08001330 @Override
1331 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001332 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001333 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001334 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001335 mUI.hidePostCaptureAlert();
Michael Kolb8872c232013-01-29 10:33:22 -08001336 setupPreview();
1337 }
1338
Michael Kolbd6954f32013-03-08 20:43:01 -08001339 @Override
1340 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001341 if (mPaused) {
1342 return;
1343 }
1344
1345 byte[] data = mJpegImageData;
1346
1347 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001348 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001349 // caller specifies a "save uri" then write the data to its
1350 // stream. Otherwise, pass back a scaled down version of the bitmap
1351 // directly in the extras.
1352 if (mSaveUri != null) {
1353 OutputStream outputStream = null;
1354 try {
1355 outputStream = mContentResolver.openOutputStream(mSaveUri);
1356 outputStream.write(data);
1357 outputStream.close();
1358
1359 mActivity.setResultEx(Activity.RESULT_OK);
1360 mActivity.finish();
1361 } catch (IOException ex) {
1362 // ignore exception
1363 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001364 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001365 }
1366 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001367 ExifInterface exif = Exif.getExif(data);
1368 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001369 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1370 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001371 mActivity.setResultEx(Activity.RESULT_OK,
1372 new Intent("inline-data").putExtra("data", bitmap));
1373 mActivity.finish();
1374 }
1375 } else {
1376 // Save the image to a temp file and invoke the cropper
1377 Uri tempUri = null;
1378 FileOutputStream tempStream = null;
1379 try {
1380 File path = mActivity.getFileStreamPath(sTempCropFilename);
1381 path.delete();
1382 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1383 tempStream.write(data);
1384 tempStream.close();
1385 tempUri = Uri.fromFile(path);
1386 } catch (FileNotFoundException ex) {
1387 mActivity.setResultEx(Activity.RESULT_CANCELED);
1388 mActivity.finish();
1389 return;
1390 } catch (IOException ex) {
1391 mActivity.setResultEx(Activity.RESULT_CANCELED);
1392 mActivity.finish();
1393 return;
1394 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001395 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001396 }
1397
1398 Bundle newExtras = new Bundle();
1399 if (mCropValue.equals("circle")) {
1400 newExtras.putString("circleCrop", "true");
1401 }
1402 if (mSaveUri != null) {
1403 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1404 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001405 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001406 }
1407 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001408 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001409 }
1410
Sascha Haeberling37f36112013-08-06 14:31:52 -07001411 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001412 final String CROP_ACTION = "com.android.camera.action.CROP";
1413 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001414
1415 cropIntent.setData(tempUri);
1416 cropIntent.putExtras(newExtras);
1417
1418 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1419 }
1420 }
1421
Michael Kolb8872c232013-01-29 10:33:22 -08001422 @Override
1423 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001424 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001425 }
1426
1427 @Override
1428 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001429 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001430 || (mCameraState == PREVIEW_STOPPED)) {
1431 return;
1432 }
Michael Kolb8872c232013-01-29 10:33:22 -08001433
1434 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001435 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001436 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001437 + mActivity.getStorageSpaceBytes());
Michael Kolb8872c232013-01-29 10:33:22 -08001438 return;
1439 }
Alan Newbergerd41766f2014-04-09 18:25:34 -07001440 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState);
Michael Kolb8872c232013-01-29 10:33:22 -08001441
Doris Liu6c751642014-05-05 18:43:26 -07001442 int countDownDuration = Integer.parseInt(mActivity.getSettingsManager()
1443 .get(SettingsManager.SETTING_COUNTDOWN_DURATION));
1444 if (countDownDuration > 0) {
1445 // Start count down.
1446 mAppController.getCameraAppUI().transitionToCancel();
1447 mAppController.getCameraAppUI().hideModeOptions();
1448 mUI.startCountdown(countDownDuration);
1449 return;
1450 } else {
1451 focusAndCapture();
1452 }
1453 }
1454
1455 private void focusAndCapture() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001456 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001457 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001458 }
Michael Kolb8872c232013-01-29 10:33:22 -08001459 // If the user wants to do a snapshot while the previous one is still
1460 // in progress, remember the fact and do it after we finish the previous
1461 // one and re-start the preview. Snapshot in progress also includes the
1462 // state that autofocus is focusing and a picture will be taken when
1463 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001464 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1465 if (!mIsImageCaptureIntent) {
1466 mSnapshotOnIdle = true;
1467 }
Michael Kolb8872c232013-01-29 10:33:22 -08001468 return;
1469 }
1470
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001471 mSnapshotOnIdle = false;
Angus Kongeaaf56d2014-02-20 11:59:10 -08001472 mFocusManager.focusAndCapture();
Michael Kolb8872c232013-01-29 10:33:22 -08001473 }
1474
Doris Liu6c751642014-05-05 18:43:26 -07001475 @Override
1476 public void onRemainingSecondsChanged(int remainingSeconds) {
1477 mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
1478 }
1479
1480 @Override
1481 public void onCountDownFinished() {
1482 mAppController.getCameraAppUI().transitionToCapture();
1483 mAppController.getCameraAppUI().showModeOptions();
1484 if (mPaused) {
1485 return;
1486 }
1487 focusAndCapture();
1488 }
1489
Andy Huibersdef975d2013-11-22 09:13:39 -08001490 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001491 if (mPaused) {
1492 return;
1493 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001494 Log.v(TAG, "Executing onResumeTasks.");
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001495 CameraProvider camProvider = mActivity.getCameraProvider();
1496 if (camProvider == null) {
1497 // No camera provider, the Activity is destroyed already.
1498 return;
1499 }
1500 camProvider.requestCamera(mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -08001501
Michael Kolb8872c232013-01-29 10:33:22 -08001502 mJpegPictureCallbackTime = 0;
1503 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001504
1505 mOnResumeTime = SystemClock.uptimeMillis();
1506 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001507
1508 // If first time initialization is not finished, put it in the
1509 // message queue.
1510 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001511 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001512 } else {
1513 initializeSecondTime();
1514 }
Michael Kolb8872c232013-01-29 10:33:22 -08001515
Angus Kong0d00a892013-03-26 11:40:40 -07001516 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1517 if (gsensor != null) {
1518 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1519 }
1520
1521 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1522 if (msensor != null) {
1523 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1524 }
Michael Kolb8872c232013-01-29 10:33:22 -08001525 }
1526
Angus Kongc4e66562013-11-22 23:03:21 -08001527 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001528 * @return Whether the currently active camera is front-facing.
1529 */
1530 private boolean isCameraFrontFacing() {
1531 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1532 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1533 }
1534
1535 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001536 * The focus manager is the first UI related element to get initialized, and
1537 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001538 */
1539 private void initializeFocusManager() {
1540 // Create FocusManager object. startPreview needs it.
1541 // if mFocusManager not null, reuse it
1542 // otherwise create a new instance
1543 if (mFocusManager != null) {
1544 mFocusManager.removeMessages();
1545 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001546 mMirror = isCameraFrontFacing();
Angus Kongc4e66562013-11-22 23:03:21 -08001547 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1548 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001549 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1550 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001551 mInitialParams, this, mMirror,
Doris Liu482de022013-12-18 19:18:16 -08001552 mActivity.getMainLooper(), mUI.getFocusUI());
Angus Kongc4e66562013-11-22 23:03:21 -08001553 }
Doris Liu482de022013-12-18 19:18:16 -08001554 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001555 }
1556
1557 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001558 public void resume() {
1559 mPaused = false;
Doris Liu6c751642014-05-05 18:43:26 -07001560 mCountdownSoundPlayer.loadSounds();
Doris Liu482de022013-12-18 19:18:16 -08001561 if (mFocusManager != null) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001562 // If camera is not open when resume is called, focus manager will
1563 // not
Doris Liu15b99612013-12-21 11:32:28 -08001564 // be initialized yet, in which case it will start listening to
1565 // preview area size change later in the initialization.
Doris Liu482de022013-12-18 19:18:16 -08001566 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1567 }
Doris Liu6c751642014-05-05 18:43:26 -07001568 mAppController.addPreviewAreaSizeChangedListener(mUI);
Doris Liu59401042014-01-14 17:51:32 -08001569
Angus Kongc4e66562013-11-22 23:03:21 -08001570 // Add delay on resume from lock screen only, in order to to speed up
1571 // the onResume --> onPause --> onResume cycle from lock screen.
1572 // Don't do always because letting go of thread can cause delay.
1573 String action = mActivity.getIntent().getAction();
1574 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1575 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1576 Log.v(TAG, "On resume, from lock screen.");
1577 // Note: onPauseAfterSuper() will delete this runnable, so we will
1578 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001579 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001580 } else {
1581 Log.v(TAG, "On resume.");
1582 onResumeTasks();
1583 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001584 getServices().getRemoteShutterListener().onModuleReady(this);
Andy Huibers10c58162014-03-29 14:06:54 -07001585 SessionStatsCollector.instance().sessionActive(true);
Angus Kongc4e66562013-11-22 23:03:21 -08001586 }
1587
1588 @Override
1589 public void pause() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001590 getServices().getRemoteShutterListener().onModuleExit();
Michael Kolb8872c232013-01-29 10:33:22 -08001591 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001592 mHandler.removeCallbacks(mResumeTaskRunnable);
Andy Huibers10c58162014-03-29 14:06:54 -07001593 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001594
Angus Kong0d00a892013-03-26 11:40:40 -07001595 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1596 if (gsensor != null) {
1597 mSensorManager.unregisterListener(this, gsensor);
1598 }
1599
1600 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1601 if (msensor != null) {
1602 mSensorManager.unregisterListener(this, msensor);
1603 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001604
Michael Kolb8872c232013-01-29 10:33:22 -08001605 // Reset the focus first. Camera CTS does not guarantee that
1606 // cancelAutoFocus is allowed after preview stops.
1607 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1608 mCameraDevice.cancelAutoFocus();
1609 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001610
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001611 // If the camera has not been opened asynchronously yet,
1612 // and startPreview hasn't been called, then this is a no-op.
1613 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001614 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001615 cancelCountDown();
1616 mCountdownSoundPlayer.release();
Michael Kolb8872c232013-01-29 10:33:22 -08001617
Angus Kongce5480e2013-01-29 17:43:48 -08001618 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001619 // If we are in an image capture intent and has taken
1620 // a picture, we just clear it in onPause.
1621 mJpegImageData = null;
1622
Angus Kongdcccc512013-08-08 17:06:03 -07001623 // Remove the messages and runnables in the queue.
1624 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001625
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001626 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001627 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001628 mUI.onPause();
1629
Michael Kolb8872c232013-01-29 10:33:22 -08001630 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001631 if (mFocusManager != null) {
1632 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001633 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001634 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001635 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001636 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001637
1638 SettingsManager settingsManager = mActivity.getSettingsManager();
1639 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001640 }
1641
Angus Kong20fad242013-11-11 18:23:46 -08001642 @Override
1643 public void destroy() {
1644 // TODO: implement this.
1645 }
1646
1647 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001648 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001649 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001650 }
1651
1652 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001653 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001654 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001655 setDisplayOrientation();
1656 }
1657 }
1658
Michael Kolb8872c232013-01-29 10:33:22 -08001659 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001660 return isCameraIdle()
1661 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001662 }
1663
1664 @Override
1665 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001666 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001667 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001668 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001669 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001670 setCameraState(FOCUSING);
1671 }
1672
1673 @Override
1674 public void cancelAutoFocus() {
1675 mCameraDevice.cancelAutoFocus();
1676 setCameraState(IDLE);
1677 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1678 }
1679
Michael Kolb8872c232013-01-29 10:33:22 -08001680 @Override
1681 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001682 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1683 || mCameraState == SNAPSHOT_IN_PROGRESS
1684 || mCameraState == SWITCHING_CAMERA
1685 || mCameraState == PREVIEW_STOPPED) {
1686 return;
1687 }
1688
1689 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001690 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1691 return;
1692 }
Doris Liu482de022013-12-18 19:18:16 -08001693 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001694 }
1695
1696 @Override
1697 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001698 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001699 }
1700
1701 @Override
1702 public boolean onKeyDown(int keyCode, KeyEvent event) {
1703 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001704 case KeyEvent.KEYCODE_VOLUME_UP:
1705 case KeyEvent.KEYCODE_VOLUME_DOWN:
1706 case KeyEvent.KEYCODE_FOCUS:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001707 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001708 if (event.getRepeatCount() == 0) {
1709 onShutterButtonFocus(true);
1710 }
1711 return true;
1712 }
1713 return false;
1714 case KeyEvent.KEYCODE_CAMERA:
1715 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1716 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001717 }
1718 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001719 case KeyEvent.KEYCODE_DPAD_CENTER:
1720 // If we get a dpad center event without any focused view, move
1721 // the focus to the shutter button and press it.
1722 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1723 // Start auto-focus immediately to reduce shutter lag. After
1724 // the shutter button gets the focus, onShutterButtonFocus()
1725 // will be called again but it is fine.
1726 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001727 }
1728 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001729 }
1730 return false;
1731 }
1732
1733 @Override
1734 public boolean onKeyUp(int keyCode, KeyEvent event) {
1735 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001736 case KeyEvent.KEYCODE_VOLUME_UP:
1737 case KeyEvent.KEYCODE_VOLUME_DOWN:
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001738 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001739 onShutterButtonClick();
1740 return true;
1741 }
1742 return false;
1743 case KeyEvent.KEYCODE_FOCUS:
1744 if (mFirstTimeInitialized) {
1745 onShutterButtonFocus(false);
1746 }
Michael Kolb8872c232013-01-29 10:33:22 -08001747 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001748 }
1749 return false;
1750 }
1751
Michael Kolb8872c232013-01-29 10:33:22 -08001752 private void closeCamera() {
1753 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001754 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001755 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001756 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001757 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001758
Michael Kolb8872c232013-01-29 10:33:22 -08001759 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001760 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001761 mCameraDevice = null;
1762 setCameraState(PREVIEW_STOPPED);
1763 mFocusManager.onCameraReleased();
1764 }
1765 }
1766
1767 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001768 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1769 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001770 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001771 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001772 if (mFocusManager != null) {
1773 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1774 }
Doris Liu6432cd62013-06-13 17:20:31 -07001775 // Change the camera display orientation
1776 if (mCameraDevice != null) {
1777 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1778 }
Michael Kolb8872c232013-01-29 10:33:22 -08001779 }
1780
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001781 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001782 private void setupPreview() {
1783 mFocusManager.resetTouchFocus();
1784 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001785 }
1786
Angus Kong20fad242013-11-11 18:23:46 -08001787 /**
1788 * Returns whether we can/should start the preview or not.
1789 */
1790 private boolean checkPreviewPreconditions() {
1791 if (mPaused) {
1792 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001793 }
Michael Kolb8872c232013-01-29 10:33:22 -08001794
Angus Kong20fad242013-11-11 18:23:46 -08001795 if (mCameraDevice == null) {
1796 Log.w(TAG, "startPreview: camera device not ready yet.");
1797 return false;
1798 }
1799
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001800 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001801 if (st == null) {
1802 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001803 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001804 }
1805
1806 if (!mCameraPreviewParamsReady) {
1807 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001808 return false;
1809 }
1810 return true;
1811 }
1812
1813 /**
1814 * The start/stop preview should only run on the UI thread.
1815 */
1816 private void startPreview() {
1817 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001818 return;
1819 }
Angus Kong20fad242013-11-11 18:23:46 -08001820
Angus Kong2bca2102014-03-11 16:27:30 -07001821 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001822 setDisplayOrientation();
1823
1824 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001825 // If the focus mode is continuous autofocus, call cancelAutoFocus
1826 // to resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001827 String focusMode = mFocusManager.getFocusMode();
1828 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001829 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001830 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001831 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1832 }
1833 setCameraParameters(UPDATE_PARAM_ALL);
Doris Liucb8215e2014-05-19 11:53:32 -07001834
1835 // Workaround for KitKat and KitKat MR1 which leave configured preview
1836 // callback streams lingering around when they should have been removed.
1837 // These preview callback streams are the cause for distorted preview.
1838 // For more details, see b/12210027
1839 if (mPreviewFirstRunInCurrentModule && ApiHelper.SHOULD_HARD_RESET_PREVIEW_CALLBACK) {
1840 // Only apply this workaround on the first entry of camera mode from
1841 // other modes.
1842 mPreviewFirstRunInCurrentModule = false;
1843 Size previewSize = new Size(mCameraDevice.getParameters().getPreviewSize());
1844 mCameraDevice.setPreviewDataCallbackWithBuffer(mHandler,
1845 new CameraManager.CameraPreviewDataCallback() {
1846 @Override
1847 public void onPreviewFrame(byte[] data, CameraProxy camera) {
1848 // Remove callback after the first frame comes in.
1849 mCameraDevice.setPreviewDataCallbackWithBuffer(null, null);
1850 }
1851 });
1852 mCameraDevice.addCallbackBuffer(new byte[previewSize.width() * previewSize.height()]);
1853 }
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001854 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001855
Alan Newbergerd41766f2014-04-09 18:25:34 -07001856 Log.i(TAG, "startPreview");
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001857 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001858
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001859 mFocusManager.onPreviewStarted();
1860 onPreviewStarted();
Andy Huibers10c58162014-03-29 14:06:54 -07001861 SessionStatsCollector.instance().previewActive(true);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001862 if (mSnapshotOnIdle) {
1863 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001864 }
1865 }
1866
Michael Kolbd6954f32013-03-08 20:43:01 -08001867 @Override
1868 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001869 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001870 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001871 mCameraDevice.stopPreview();
1872 mFaceDetectionStarted = false;
1873 }
1874 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001875 if (mFocusManager != null) {
1876 mFocusManager.onPreviewStopped();
1877 }
Andy Huibers10c58162014-03-29 14:06:54 -07001878 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001879 }
1880
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001881 @Override
Erin Dahlgren1648c362014-01-06 15:06:04 -08001882 public void onSettingChanged(SettingsManager settingsManager, int id) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001883 switch (id) {
1884 case SettingsManager.SETTING_FLASH_MODE: {
1885 updateParametersFlashMode();
1886 break;
1887 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001888 default: {
1889 // Do nothing.
1890 }
1891 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001892
1893 if (mCameraDevice != null) {
1894 mCameraDevice.setParameters(mParameters);
1895 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001896 }
1897
Michael Kolb8872c232013-01-29 10:33:22 -08001898 private void updateCameraParametersInitialize() {
1899 // Reset preview frame rate to the maximum because it may be lowered by
1900 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07001901 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07001902 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001903 mParameters.setPreviewFpsRange(
1904 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1905 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001906 }
1907
Angus Kongb50b5cb2013-08-09 14:55:20 -07001908 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001909
1910 // Disable video stabilization. Convenience methods not available in API
1911 // level <= 14
1912 String vstabSupported = mParameters.get("video-stabilization-supported");
1913 if ("true".equals(vstabSupported)) {
1914 mParameters.set("video-stabilization", "false");
1915 }
1916 }
1917
1918 private void updateCameraParametersZoom() {
1919 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07001920 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001921 mParameters.setZoom(mZoomValue);
1922 }
1923 }
1924
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001925 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001926 private void setAutoExposureLockIfSupported() {
1927 if (mAeLockSupported) {
1928 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1929 }
1930 }
1931
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001932 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001933 private void setAutoWhiteBalanceLockIfSupported() {
1934 if (mAwbLockSupported) {
1935 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1936 }
1937 }
1938
Michael Kolb8872c232013-01-29 10:33:22 -08001939 private void setFocusAreasIfSupported() {
1940 if (mFocusAreaSupported) {
1941 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1942 }
1943 }
1944
Michael Kolb8872c232013-01-29 10:33:22 -08001945 private void setMeteringAreasIfSupported() {
1946 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001947 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1948 }
1949 }
1950
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08001951 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08001952 setAutoExposureLockIfSupported();
1953 setAutoWhiteBalanceLockIfSupported();
1954 setFocusAreasIfSupported();
1955 setMeteringAreasIfSupported();
1956
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001957 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07001958 mFocusManager.overrideFocusMode(null);
1959 mParameters.setFocusMode(mFocusManager.getFocusMode());
Andy Huibers10c58162014-03-29 14:06:54 -07001960 SessionStatsCollector.instance().autofocusActive(
1961 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolbd3253f22013-07-12 11:36:47 -07001962
Michael Kolb8872c232013-01-29 10:33:22 -08001963 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001964 updateParametersPictureSize();
1965
1966 // Set JPEG quality.
1967 updateParametersPictureQuality();
1968
1969 // For the following settings, we need to check if the settings are
1970 // still supported by latest driver, if not, ignore the settings.
1971
1972 // Set exposure compensation
1973 updateParametersExposureCompensation();
1974
1975 // Set the scene mode: also sets flash and white balance.
1976 updateParametersSceneMode();
1977
1978 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
1979 updateAutoFocusMoveCallback();
1980 }
1981 }
1982
1983 private void updateParametersPictureSize() {
1984 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001985 String pictureSize = settingsManager
1986 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
1987 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001988
Angus Kong63424662014-04-23 10:47:47 -07001989 List<Size> supported = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001990 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
1991 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07001992
1993 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
1994 mCameraDevice.getCameraId());
1995 if (ApiHelper.IS_NEXUS_5) {
1996 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
1997 mShouldResizeTo16x9 = true;
1998 } else {
1999 mShouldResizeTo16x9 = false;
2000 }
2001 }
Michael Kolb8872c232013-01-29 10:33:22 -08002002
2003 // Set a preview size that is closest to the viewfinder height and has
2004 // the right aspect ratio.
Angus Kong63424662014-04-23 10:47:47 -07002005 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPreviewSizes());
Angus Kongb50b5cb2013-08-09 14:55:20 -07002006 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002007 (double) size.width() / size.height());
Angus Kong63424662014-04-23 10:47:47 -07002008 Size original = new Size(mParameters.getPreviewSize());
Michael Kolb8872c232013-01-29 10:33:22 -08002009 if (!original.equals(optimalSize)) {
Angus Kong00b7b102014-04-24 15:46:52 -07002010 mParameters.setPreviewSize(optimalSize.width(), optimalSize.height());
Doris Liu6432cd62013-06-13 17:20:31 -07002011
Michael Kolb8872c232013-01-29 10:33:22 -08002012 // Zoom related settings will be changed for different preview
2013 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002014 if (mHandler.getLooper() == Looper.myLooper()) {
2015 // On UI thread only, not when camera starts up
2016 setupPreview();
2017 } else {
2018 mCameraDevice.setParameters(mParameters);
2019 }
Michael Kolb8872c232013-01-29 10:33:22 -08002020 mParameters = mCameraDevice.getParameters();
2021 }
Doris Liu95405742013-11-05 15:25:26 -08002022
Angus Kong00b7b102014-04-24 15:46:52 -07002023 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2024 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2025 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002026 }
Angus Kong00b7b102014-04-24 15:46:52 -07002027 Log.i(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002028 }
Michael Kolb8872c232013-01-29 10:33:22 -08002029
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002030 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002031 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2032 CameraProfile.QUALITY_HIGH);
2033 mParameters.setJpegQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002034 }
Michael Kolb8872c232013-01-29 10:33:22 -08002035
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002036 private void updateParametersExposureCompensation() {
2037 SettingsManager settingsManager = mActivity.getSettingsManager();
Spike Spragueabf54e22014-03-27 15:41:28 -07002038 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
2039 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
Angus Kong88289042014-04-22 16:39:42 -07002040 int max = mCameraCapabilities.getMaxExposureCompensation();
2041 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002042 if (value >= min && value <= max) {
2043 mParameters.setExposureCompensation(value);
2044 } else {
2045 Log.w(TAG, "invalid exposure range: " + value);
2046 }
Michael Kolb8872c232013-01-29 10:33:22 -08002047 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002048 // If exposure compensation is not enabled, reset the exposure compensation value.
2049 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002050 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002051
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002052 }
2053
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002054 private void updateParametersSceneMode() {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002055 SettingsManager settingsManager = mActivity.getSettingsManager();
2056
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002057 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
Angus Kong88289042014-04-22 16:39:42 -07002058 if (mCameraCapabilities
2059 .supports(mCameraCapabilities.getStringifier().sceneModeFromString(mSceneMode))) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002060 if (!mParameters.getSceneMode().equals(mSceneMode)) {
2061 mParameters.setSceneMode(mSceneMode);
2062
2063 // Setting scene mode will change the settings of flash mode,
2064 // white balance, and focus mode. Here we read back the
2065 // parameters, so we can know those settings.
2066 mCameraDevice.setParameters(mParameters);
2067 mParameters = mCameraDevice.getParameters();
2068 }
2069 } else {
2070 mSceneMode = mParameters.getSceneMode();
2071 if (mSceneMode == null) {
2072 mSceneMode = Parameters.SCENE_MODE_AUTO;
2073 }
2074 }
2075
Michael Kolb8872c232013-01-29 10:33:22 -08002076 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
2077 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002078 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002079
Michael Kolb8872c232013-01-29 10:33:22 -08002080 // Set focus mode.
2081 mFocusManager.overrideFocusMode(null);
2082 mParameters.setFocusMode(mFocusManager.getFocusMode());
2083 } else {
2084 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
2085 }
Michael Kolb8872c232013-01-29 10:33:22 -08002086 }
2087
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002088 private void updateParametersFlashMode() {
2089 SettingsManager settingsManager = mActivity.getSettingsManager();
2090
2091 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
Angus Kong88289042014-04-22 16:39:42 -07002092 if (mCameraCapabilities
2093 .supports(mCameraCapabilities.getStringifier().flashModeFromString(flashMode))) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002094 mParameters.setFlashMode(flashMode);
2095 }
2096 }
2097
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002098 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002099 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07002100 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07002101 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002102 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002103 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002104 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002105 }
2106 }
2107
Spike Spragueabf54e22014-03-27 15:41:28 -07002108 /**
2109 * Sets the exposure compensation to the given value and also updates settings.
2110 *
2111 * @param value exposure compensation value to be set
2112 */
2113 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002114 int max = mCameraCapabilities.getMaxExposureCompensation();
2115 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002116 if (value >= min && value <= max) {
2117 mParameters.setExposureCompensation(value);
2118 SettingsManager settingsManager = mActivity.getSettingsManager();
2119 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
2120 } else {
2121 Log.w(TAG, "invalid exposure range: " + value);
2122 }
2123 }
2124
Michael Kolb8872c232013-01-29 10:33:22 -08002125 // We separate the parameters into several subsets, so we can update only
2126 // the subsets actually need updating. The PREFERENCE set needs extra
2127 // locking because the preference can be changed from GLThread as well.
2128 private void setCameraParameters(int updateSet) {
2129 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2130 updateCameraParametersInitialize();
2131 }
2132
2133 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2134 updateCameraParametersZoom();
2135 }
2136
2137 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002138 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002139 }
2140
2141 mCameraDevice.setParameters(mParameters);
2142 }
2143
2144 // If the Camera is idle, update the parameters immediately, otherwise
2145 // accumulate them in mUpdateSet and update later.
2146 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2147 mUpdateSet |= additionalUpdateSet;
2148 if (mCameraDevice == null) {
2149 // We will update all the parameters when we open the device, so
2150 // we don't need to do anything now.
2151 mUpdateSet = 0;
2152 return;
2153 } else if (isCameraIdle()) {
2154 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002155 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002156 mUpdateSet = 0;
2157 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002158 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2159 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002160 }
2161 }
2162 }
2163
ztenghui7b265a62013-09-09 14:58:44 -07002164 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002165 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002166 return (mCameraState == IDLE) ||
2167 (mCameraState == PREVIEW_STOPPED) ||
2168 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002169 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002170 }
2171
ztenghui7b265a62013-09-09 14:58:44 -07002172 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002173 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002174 String action = mActivity.getIntent().getAction();
2175 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002176 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002177 }
2178
2179 private void setupCaptureParams() {
2180 Bundle myExtras = mActivity.getIntent().getExtras();
2181 if (myExtras != null) {
2182 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2183 mCropValue = myExtras.getString("crop");
2184 }
2185 }
2186
Michael Kolb8872c232013-01-29 10:33:22 -08002187 private void initializeCapabilities() {
2188 mInitialParams = mCameraDevice.getParameters();
Angus Kong88289042014-04-22 16:39:42 -07002189 mCameraCapabilities = mCameraDevice.getCapabilities();
2190 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2191 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2192 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2193 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kongdcccc512013-08-08 17:06:03 -07002194 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07002195 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002196 }
2197
Marco Nelissen0744e4a2013-11-22 01:47:37 +00002198 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08002199 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002200 public int onZoomChanged(int index) {
2201 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002202 if (mPaused) {
2203 return index;
2204 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002205 mZoomValue = index;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002206 if (mParameters == null || mCameraDevice == null) {
2207 return index;
2208 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002209 // Set zoom parameters asynchronously
2210 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07002211 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08002212 Parameters p = mCameraDevice.getParameters();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002213 if (p != null) {
2214 return p.getZoom();
2215 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002216 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002217 }
2218
2219 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002220 public int getCameraState() {
2221 return mCameraState;
2222 }
2223
2224 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002225 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002226 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002227 }
Angus Kong86d36312013-01-31 18:22:44 -08002228
2229 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002230 public void onLowMemory() {
2231 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002232 }
Angus Kong0d00a892013-03-26 11:40:40 -07002233
2234 @Override
2235 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2236 }
2237
2238 @Override
2239 public void onSensorChanged(SensorEvent event) {
2240 int type = event.sensor.getType();
2241 float[] data;
2242 if (type == Sensor.TYPE_ACCELEROMETER) {
2243 data = mGData;
2244 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2245 data = mMData;
2246 } else {
2247 // we should not be here.
2248 return;
2249 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002250 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002251 data[i] = event.values[i];
2252 }
2253 float[] orientation = new float[3];
2254 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2255 SensorManager.getOrientation(mR, orientation);
2256 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2257 if (mHeading < 0) {
2258 mHeading += 360;
2259 }
Angus Kong0d00a892013-03-26 11:40:40 -07002260 }
Doris Liu6432cd62013-06-13 17:20:31 -07002261
Ruben Brunkd217ed02013-10-08 23:31:13 -07002262 // For debugging only.
2263 public void setDebugUri(Uri uri) {
2264 mDebugUri = uri;
2265 }
2266
2267 // For debugging only.
2268 private void saveToDebugUri(byte[] data) {
2269 if (mDebugUri != null) {
2270 OutputStream outputStream = null;
2271 try {
2272 outputStream = mContentResolver.openOutputStream(mDebugUri);
2273 outputStream.write(data);
2274 outputStream.close();
2275 } catch (IOException e) {
2276 Log.e(TAG, "Exception while writing debug jpeg file", e);
2277 } finally {
2278 CameraUtil.closeSilently(outputStream);
2279 }
2280 }
2281 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002282
2283 @Override
2284 public void onRemoteShutterPress() {
2285 capture();
2286 }
Doris Liu6c751642014-05-05 18:43:26 -07002287
2288 /**
2289 * This class manages the loading/releasing/playing of the sounds needed for
2290 * countdown timer.
2291 */
2292 private class CountdownSoundPlayer {
2293 private SoundPool mSoundPool;
2294 private int mBeepOnce;
2295 private int mBeepTwice;
2296
2297 void loadSounds() {
2298 // Load the beeps.
2299 mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2300 mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
2301 mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
2302 }
2303
2304 void onRemainingSecondsChanged(int newVal) {
2305 if (mSoundPool == null) {
2306 Log.e(TAG, "Cannot play sound - they have not been loaded.");
2307 return;
2308 }
2309 if (newVal == 1) {
2310 mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
2311 } else if (newVal == 2 || newVal == 3) {
2312 mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
2313 }
2314 }
2315
2316 void release() {
2317 mSoundPool.release();
2318 mSoundPool = null;
2319 }
2320 }
Michael Kolb8872c232013-01-29 10:33:22 -08002321}