blob: 4b90ea1542a335374003e5cd0df1bc7255922793 [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;
Kevin Gabayanfb333362014-06-02 14:48:20 -070059import com.android.camera.app.MotionManager;
Angus Kong88289042014-04-22 16:39:42 -070060import com.android.camera.cameradevice.CameraCapabilities;
Angus Kong1045fef2014-04-21 11:17:37 -070061import com.android.camera.cameradevice.CameraManager.CameraAFCallback;
62import com.android.camera.cameradevice.CameraManager.CameraAFMoveCallback;
63import com.android.camera.cameradevice.CameraManager.CameraPictureCallback;
64import com.android.camera.cameradevice.CameraManager.CameraProxy;
65import com.android.camera.cameradevice.CameraManager.CameraShutterCallback;
Angus Kong2bca2102014-03-11 16:27:30 -070066import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070067import com.android.camera.exif.ExifInterface;
68import com.android.camera.exif.ExifTag;
69import com.android.camera.exif.Rational;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080070import com.android.camera.hardware.HardwareSpec;
71import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080072import com.android.camera.module.ModuleController;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -080073import com.android.camera.remote.RemoteCameraModule;
Andy Huibers90c7ad52014-05-20 22:13:54 -070074import com.android.camera.settings.CameraPictureSizesCacher;
Angus Kong454d63f2014-05-06 14:45:59 -070075import com.android.camera.settings.ResolutionUtil;
Erin Dahlgren357b7672013-11-20 17:38:14 -080076import com.android.camera.settings.SettingsManager;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +010077import com.android.camera.settings.SettingsUtil;
Doris Liu6c751642014-05-05 18:43:26 -070078import com.android.camera.ui.CountDownView;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -070079import com.android.camera.ui.TouchCoordinate;
Angus Kongdcccc512013-08-08 17:06:03 -070080import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070081import com.android.camera.util.CameraUtil;
Ruben Brunk4601f5d2013-09-24 18:35:22 -070082import com.android.camera.util.GcamHelper;
Andy Huibers10c58162014-03-29 14:06:54 -070083import com.android.camera.util.SessionStatsCollector;
Angus Kong63424662014-04-23 10:47:47 -070084import com.android.camera.util.Size;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070085import com.android.camera.util.UsageStatistics;
Doris Liudb8f9752014-05-12 15:25:13 -070086import com.android.camera.widget.AspectRatioSelector;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070087import com.android.camera2.R;
Seth Raphael5e09d012013-12-18 13:45:03 -080088import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080089
Angus Kong454d63f2014-05-06 14:45:59 -070090import java.io.ByteArrayOutputStream;
Angus Kongdcccc512013-08-08 17:06:03 -070091import java.io.File;
92import java.io.FileNotFoundException;
93import java.io.FileOutputStream;
94import java.io.IOException;
95import java.io.OutputStream;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +010096import java.lang.ref.WeakReference;
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -070097import java.text.DecimalFormat;
Angus Kongdcccc512013-08-08 17:06:03 -070098import java.util.List;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070099import java.util.Vector;
Angus Kongdcccc512013-08-08 17:06:03 -0700100
Michael Kolb8872c232013-01-29 10:33:22 -0800101public class PhotoModule
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800102 extends CameraModule
103 implements PhotoController,
104 ModuleController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800105 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800106 FocusOverlayManager.Listener,
Erin Dahlgren7f0151d2014-01-02 16:08:12 -0800107 SensorEventListener,
Sascha Haeberlingf2994f02014-01-23 19:55:01 -0800108 SettingsManager.OnSettingChangedListener,
Doris Liu6c751642014-05-05 18:43:26 -0700109 RemoteCameraModule,
110 CountDownView.OnCountDownStatusListener {
Michael Kolb8872c232013-01-29 10:33:22 -0800111
Angus Kong2bca2102014-03-11 16:27:30 -0700112 private static final Log.Tag TAG = new Log.Tag("PhotoModule");
Michael Kolb8872c232013-01-29 10:33:22 -0800113
114 // We number the request code from 1000 to avoid collision with Gallery.
115 private static final int REQUEST_CROP = 1000;
116
Angus Kong13e87c42013-11-25 10:02:47 -0800117 // Messages defined for the UI thread handler.
Angus Kong1587b042014-01-02 18:09:27 -0800118 private static final int MSG_FIRST_TIME_INIT = 1;
119 private static final int MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE = 2;
Michael Kolb8872c232013-01-29 10:33:22 -0800120
121 // The subset of parameters we need to update in setCameraParameters().
122 private static final int UPDATE_PARAM_INITIALIZE = 1;
123 private static final int UPDATE_PARAM_ZOOM = 2;
124 private static final int UPDATE_PARAM_PREFERENCE = 4;
125 private static final int UPDATE_PARAM_ALL = -1;
126
Andy Huibersdef975d2013-11-22 09:13:39 -0800127 // This is the delay before we execute onResume tasks when coming
128 // from the lock screen, to allow time for onPause to execute.
129 private static final int ON_RESUME_TASKS_DELAY_MSEC = 20;
Michael Kolb8872c232013-01-29 10:33:22 -0800130
Ruben Brunkd7488272013-10-10 18:45:53 -0700131 private static final String DEBUG_IMAGE_PREFIX = "DEBUG_";
132
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700133 private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0");
134
Michael Kolb8872c232013-01-29 10:33:22 -0800135 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800136 private CameraProxy mCameraDevice;
137 private int mCameraId;
Angus Kong88289042014-04-22 16:39:42 -0700138 private CameraCapabilities mCameraCapabilities;
Michael Kolb8872c232013-01-29 10:33:22 -0800139 private Parameters mParameters;
140 private boolean mPaused;
Michael Kolbd6954f32013-03-08 20:43:01 -0800141
142 private PhotoUI mUI;
Michael Kolb8872c232013-01-29 10:33:22 -0800143
Michael Kolb8872c232013-01-29 10:33:22 -0800144 // The activity is going to switch to the specified camera id. This is
145 // needed because texture copy is done in GL thread. -1 means camera is not
146 // switching.
147 protected int mPendingSwitchCameraId = -1;
Michael Kolb8872c232013-01-29 10:33:22 -0800148
149 // When setCameraParametersWhenIdle() is called, we accumulate the subsets
150 // needed to be updated in mUpdateSet.
151 private int mUpdateSet;
152
153 private static final int SCREEN_DELAY = 2 * 60 * 1000;
154
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800155 private int mZoomValue; // The current zoom value.
Andy Huibers547d7c82014-05-20 23:03:18 -0700156 private int mTimerDuration;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700157 /** Set when a volume button is clicked to take photo */
158 private boolean mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800159
160 private Parameters mInitialParams;
161 private boolean mFocusAreaSupported;
162 private boolean mMeteringAreaSupported;
163 private boolean mAeLockSupported;
164 private boolean mAwbLockSupported;
Angus Kongdcccc512013-08-08 17:06:03 -0700165 private boolean mContinuousFocusSupported;
Michael Kolb8872c232013-01-29 10:33:22 -0800166
167 // The degrees of the device rotated clockwise from its natural orientation.
168 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
Michael Kolb8872c232013-01-29 10:33:22 -0800169
170 private static final String sTempCropFilename = "crop-temp";
171
Michael Kolb8872c232013-01-29 10:33:22 -0800172 private boolean mFaceDetectionStarted = false;
173
Michael Kolb8872c232013-01-29 10:33:22 -0800174 // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
175 private String mCropValue;
176 private Uri mSaveUri;
177
Ruben Brunkd217ed02013-10-08 23:31:13 -0700178 private Uri mDebugUri;
179
Angus Kongce5480e2013-01-29 17:43:48 -0800180 // We use a queue to generated names of the images to be used later
181 // when the image is ready to be saved.
Michael Kolb8872c232013-01-29 10:33:22 -0800182 private NamedImages mNamedImages;
183
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800184 private final Runnable mDoSnapRunnable = new Runnable() {
Michael Kolb8872c232013-01-29 10:33:22 -0800185 @Override
186 public void run() {
187 onShutterButtonClick();
188 }
189 };
190
Michael Kolb8872c232013-01-29 10:33:22 -0800191 /**
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800192 * An unpublished intent flag requesting to return as soon as capturing is
193 * completed. TODO: consider publishing by moving into MediaStore.
Michael Kolb8872c232013-01-29 10:33:22 -0800194 */
195 private static final String EXTRA_QUICK_CAPTURE =
196 "android.intent.extra.quickCapture";
197
198 // The display rotation in degrees. This is only valid when mCameraState is
199 // not PREVIEW_STOPPED.
200 private int mDisplayRotation;
201 // The value for android.hardware.Camera.setDisplayOrientation.
202 private int mCameraDisplayOrientation;
203 // The value for UI components like indicators.
204 private int mDisplayOrientation;
205 // The value for android.hardware.Camera.Parameters.setRotation.
206 private int mJpegRotation;
Doris Liu29da2db2013-08-28 14:28:45 -0700207 // Indicates whether we are using front camera
208 private boolean mMirror;
Michael Kolb8872c232013-01-29 10:33:22 -0800209 private boolean mFirstTimeInitialized;
210 private boolean mIsImageCaptureIntent;
211
Michael Kolb8872c232013-01-29 10:33:22 -0800212 private int mCameraState = PREVIEW_STOPPED;
213 private boolean mSnapshotOnIdle = false;
214
215 private ContentResolver mContentResolver;
216
217 private LocationManager mLocationManager;
Doris Liu2b906b82013-12-10 16:34:08 -0800218 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800219
Michael Kolb8872c232013-01-29 10:33:22 -0800220 private final PostViewPictureCallback mPostViewPictureCallback =
221 new PostViewPictureCallback();
222 private final RawPictureCallback mRawPictureCallback =
223 new RawPictureCallback();
224 private final AutoFocusCallback mAutoFocusCallback =
225 new AutoFocusCallback();
226 private final Object mAutoFocusMoveCallback =
227 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700228 ? new AutoFocusMoveCallback()
229 : null;
Michael Kolb8872c232013-01-29 10:33:22 -0800230
231 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
232
233 private long mFocusStartTime;
234 private long mShutterCallbackTime;
235 private long mPostViewPictureCallbackTime;
236 private long mRawPictureCallbackTime;
237 private long mJpegPictureCallbackTime;
238 private long mOnResumeTime;
239 private byte[] mJpegImageData;
Andy Huibersb7c7d9a2014-06-18 22:26:14 -0700240 /** Touch coordinate for shutter button press. */
241 private TouchCoordinate mShutterTouchCoordinate;
242
Michael Kolb8872c232013-01-29 10:33:22 -0800243
244 // These latency time are for the CameraLatency test.
245 public long mAutoFocusTime;
246 public long mShutterLag;
247 public long mShutterToPictureDisplayedTime;
248 public long mPictureDisplayedToJpegCallbackTime;
249 public long mJpegCallbackFinishTime;
250 public long mCaptureStartTime;
251
252 // This handles everything about focus.
253 private FocusOverlayManager mFocusManager;
254
Doris Liubd1b8f92014-01-03 17:59:51 -0800255 private final int mGcamModeIndex;
Doris Liu6c751642014-05-05 18:43:26 -0700256 private final CountdownSoundPlayer mCountdownSoundPlayer = new CountdownSoundPlayer();
Doris Liubd1b8f92014-01-03 17:59:51 -0800257
Michael Kolb8872c232013-01-29 10:33:22 -0800258 private String mSceneMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800259
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100260 private final Handler mHandler = new MainHandler(this);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800261
Michael Kolb8872c232013-01-29 10:33:22 -0800262 private boolean mQuickCapture;
Angus Kong0d00a892013-03-26 11:40:40 -0700263 private SensorManager mSensorManager;
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800264 private final float[] mGData = new float[3];
265 private final float[] mMData = new float[3];
266 private final float[] mR = new float[16];
Angus Kong0d00a892013-03-26 11:40:40 -0700267 private int mHeading = -1;
268
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800269 /** True if all the parameters needed to start preview is ready. */
Angus Kongdcccc512013-08-08 17:06:03 -0700270 private boolean mCameraPreviewParamsReady = false;
Doris Liu6432cd62013-06-13 17:20:31 -0700271
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800272 private final MediaSaver.OnMediaSavedListener mOnMediaSavedListener =
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800273 new MediaSaver.OnMediaSavedListener() {
Angus Kongce5480e2013-01-29 17:43:48 -0800274 @Override
275 public void onMediaSaved(Uri uri) {
276 if (uri != null) {
Doris Liu6432cd62013-06-13 17:20:31 -0700277 mActivity.notifyNewMedia(uri);
Angus Kongce5480e2013-01-29 17:43:48 -0800278 }
279 }
280 };
Angus Kong454d63f2014-05-06 14:45:59 -0700281 private boolean mShouldResizeTo16x9 = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800282
Angus Kong0b9eb5b2014-04-30 15:03:33 -0700283 private final Runnable mResumeTaskRunnable = new Runnable() {
284 @Override
285 public void run() {
286 onResumeTasks();
287 }
288 };
289
Doris Liudb8f9752014-05-12 15:25:13 -0700290 /**
Angus Kong3699c412014-05-23 15:31:09 -0700291 * We keep the flash setting before entering scene modes (HDR)
292 * and restore it after HDR is off.
293 */
294 private String mFlashModeBeforeSceneMode;
295
296 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700297 * This callback gets called when user select whether or not to
298 * turn on geo-tagging.
299 */
300 public interface LocationDialogCallback {
301 /**
302 * Gets called after user selected/unselected geo-tagging feature.
303 *
304 * @param selected whether or not geo-tagging feature is selected
305 */
306 public void onLocationTaggingSelected(boolean selected);
307 }
308
309 /**
310 * This callback defines the text that is shown in the aspect ratio selection
311 * dialog, provides the current aspect ratio, and gets notified when user changes
312 * aspect ratio selection in the dialog.
313 */
314 public interface AspectRatioDialogCallback {
315 /**
Doris Liudb8f9752014-05-12 15:25:13 -0700316 * Returns current aspect ratio that is being used to set as default.
317 */
318 public AspectRatioSelector.AspectRatio getCurrentAspectRatio();
319
320 /**
321 * Gets notified when user has made the aspect ratio selection.
322 *
323 * @param newAspectRatio aspect ratio that user has selected
Doris Liu08b0cdd2014-05-13 19:19:55 -0700324 * @param dialogHandlingFinishedRunnable runnable to run when the operations
325 * needed to handle changes from dialog
326 * are finished.
Doris Liudb8f9752014-05-12 15:25:13 -0700327 */
Doris Liu08b0cdd2014-05-13 19:19:55 -0700328 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
329 Runnable dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700330 }
331
Angus Kongdcccc512013-08-08 17:06:03 -0700332 private void checkDisplayRotation() {
333 // Set the display orientation if display rotation has changed.
334 // Sometimes this happens when the device is held upside
335 // down and camera app is opened. Rotation animation will
336 // take some time and the rotation value we have got may be
337 // wrong. Framework does not have a callback for this now.
338 if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) {
339 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -0800340 }
Angus Kongdcccc512013-08-08 17:06:03 -0700341 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
342 mHandler.postDelayed(new Runnable() {
343 @Override
344 public void run() {
345 checkDisplayRotation();
346 }
347 }, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800348 }
349 }
350
351 /**
352 * This Handler is used to post message back onto the main thread of the
353 * application
354 */
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100355 private static class MainHandler extends Handler {
356 private final WeakReference<PhotoModule> mModule;
357
358 public MainHandler(PhotoModule module) {
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800359 super(Looper.getMainLooper());
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100360 mModule = new WeakReference<PhotoModule>(module);
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -0800361 }
362
Michael Kolb8872c232013-01-29 10:33:22 -0800363 @Override
364 public void handleMessage(Message msg) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100365 PhotoModule module = mModule.get();
366 if (module == null) {
367 return;
368 }
Michael Kolb8872c232013-01-29 10:33:22 -0800369 switch (msg.what) {
Angus Kong13e87c42013-11-25 10:02:47 -0800370 case MSG_FIRST_TIME_INIT: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100371 module.initializeFirstTime();
Michael Kolb8872c232013-01-29 10:33:22 -0800372 break;
373 }
374
Angus Kong13e87c42013-11-25 10:02:47 -0800375 case MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE: {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100376 module.setCameraParametersWhenIdle(0);
Michael Kolb8872c232013-01-29 10:33:22 -0800377 break;
378 }
Michael Kolb8872c232013-01-29 10:33:22 -0800379 }
380 }
381 }
382
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700383 private void switchToGcamCapture() {
384 if (mActivity != null && mGcamModeIndex != 0) {
385 SettingsManager settingsManager = mActivity.getSettingsManager();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700386 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS,
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700387 SettingsManager.VALUE_ON);
388
389 // Disable the HDR+ button to prevent callbacks from being
390 // queued before the correct callback is attached to the button
391 // in the new module. The new module will set the enabled/disabled
392 // of this button when the module's preferred camera becomes available.
393 ButtonManager buttonManager = mActivity.getButtonManager();
Erin Dahlgren15473d42014-05-14 14:01:31 -0700394 buttonManager.disableButton(ButtonManager.BUTTON_HDR_PLUS);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700395
Seth Raphael274f6e92014-05-21 17:11:53 -0700396 mAppController.getCameraAppUI().freezeScreenUntilPreviewReady();
397
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700398 // Do not post this to avoid this module switch getting interleaved with
399 // other button callbacks.
400 mActivity.onModeSelected(mGcamModeIndex);
401 }
402 }
403
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800404 /**
405 * Constructs a new photo module.
406 */
Angus Kongc4e66562013-11-22 23:03:21 -0800407 public PhotoModule(AppController app) {
408 super(app);
Doris Liubd1b8f92014-01-03 17:59:51 -0800409 mGcamModeIndex = app.getAndroidContext().getResources()
410 .getInteger(R.integer.camera_mode_gcam);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800411 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -0700412
Michael Kolb8872c232013-01-29 10:33:22 -0800413 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100414 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
415 mActivity = activity;
416 // TODO: Need to look at the controller interface to see if we can get
417 // rid of passing in the activity directly.
418 mAppController = mActivity;
419
420 mUI = new PhotoUI(mActivity, this, mActivity.getModuleLayoutRoot());
421 mActivity.setPreviewStatusListener(mUI);
Erin Dahlgren357b7672013-11-20 17:38:14 -0800422
423 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800424 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800425
Doris Liudb8f9752014-05-12 15:25:13 -0700426 // TODO: Move this to SettingsManager as a part of upgrade procedure.
427 if (!settingsManager.getBoolean(SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO)) {
428 // Switch to back camera to set aspect ratio.
429 mCameraId = Integer.parseInt(settingsManager
430 .getDefaultCameraIdSetting(activity).getDefault());
431 }
432
Michael Kolb8872c232013-01-29 10:33:22 -0800433 mContentResolver = mActivity.getContentResolver();
434
Michael Kolb8872c232013-01-29 10:33:22 -0800435 // Surface texture is from camera screen nail and startPreview needs it.
436 // This must be done before startPreview.
437 mIsImageCaptureIntent = isImageCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800438
Angus Kong20fad242013-11-11 18:23:46 -0800439 mActivity.getCameraProvider().requestCamera(mCameraId);
440
Michael Kolb8872c232013-01-29 10:33:22 -0800441 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800442 mLocationManager = mActivity.getLocationManager();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800443 mSensorManager = (SensorManager) (mActivity.getSystemService(Context.SENSOR_SERVICE));
Doris Liu6c751642014-05-05 18:43:26 -0700444 mUI.setCountdownFinishedListener(this);
445
446 // TODO: Make this a part of app controller API.
447 View cancelButton = mActivity.findViewById(R.id.shutter_cancel_button);
448 cancelButton.setOnClickListener(new View.OnClickListener() {
449 @Override
450 public void onClick(View view) {
451 cancelCountDown();
452 }
453 });
454 }
455
456 private void cancelCountDown() {
457 if (mUI.isCountingDown()) {
458 // Cancel on-going countdown.
459 mUI.cancelCountDown();
460 }
461 mAppController.getCameraAppUI().transitionToCapture();
462 mAppController.getCameraAppUI().showModeOptions();
Michael Kolbd6954f32013-03-08 20:43:01 -0800463 }
464
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800465 @Override
466 public boolean isUsingBottomBar() {
467 return true;
468 }
469
Michael Kolbd6954f32013-03-08 20:43:01 -0800470 private void initializeControlByIntent() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800471 if (mIsImageCaptureIntent) {
Spike Sprague15690d02014-02-10 12:11:20 -0800472 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Michael Kolbd6954f32013-03-08 20:43:01 -0800473 setupCaptureParams();
474 }
475 }
476
477 private void onPreviewStarted() {
Doris Liu2b906b82013-12-10 16:34:08 -0800478 mAppController.onPreviewStarted();
Michael Kolbd6954f32013-03-08 20:43:01 -0800479 setCameraState(IDLE);
Michael Kolbd6954f32013-03-08 20:43:01 -0800480 startFaceDetection();
Doris Liudb8f9752014-05-12 15:25:13 -0700481 settingsFirstRun();
Seth Raphael30836422014-02-06 14:49:47 -0800482 }
483
Doris Liudb8f9752014-05-12 15:25:13 -0700484 /**
485 * Prompt the user to pick to record location and choose aspect ratio for the
486 * very first run of camera only.
487 */
488 private void settingsFirstRun() {
489 final SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100490
Doris Liudb8f9752014-05-12 15:25:13 -0700491 if (mActivity.isSecureCamera() || isImageCaptureIntent()) {
Erin Dahlgren4569b702014-02-24 14:21:11 -0800492 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800493 }
Doris Liudb8f9752014-05-12 15:25:13 -0700494
495 boolean locationPrompt = !settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION);
496 boolean aspectRatioPrompt = !settingsManager.getBoolean(
497 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO);
498 if (!locationPrompt && !aspectRatioPrompt) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800499 return;
500 }
Doris Liudb8f9752014-05-12 15:25:13 -0700501
Michael Kolb8872c232013-01-29 10:33:22 -0800502 // Check if the back camera exists
Angus Kongd74e6a12014-01-07 11:29:44 -0800503 int backCameraId = mAppController.getCameraProvider().getFirstBackCameraId();
Michael Kolb8872c232013-01-29 10:33:22 -0800504 if (backCameraId == -1) {
505 // If there is no back camera, do not show the prompt.
506 return;
507 }
Doris Liudb8f9752014-05-12 15:25:13 -0700508
509 if (locationPrompt) {
510 // Show both location and aspect ratio selection dialog.
511 mUI.showLocationAndAspectRatioDialog(new LocationDialogCallback(){
Sascha Haeberlingb32af4f2014-05-16 08:22:00 -0700512 @Override
Doris Liudb8f9752014-05-12 15:25:13 -0700513 public void onLocationTaggingSelected(boolean selected) {
514 settingsManager.setLocation(selected, mActivity.getLocationManager());
515 }
516 }, createAspectRatioDialogCallback());
517 } else {
518 // App upgrade. Only show aspect ratio selection.
519 mUI.showAspectRatioDialog(createAspectRatioDialogCallback());
520 }
521 }
522
523 private AspectRatioDialogCallback createAspectRatioDialogCallback() {
524 Size currentSize = new Size(mParameters.getPictureSize());
525 float aspectRatio = (float) currentSize.width() / (float) currentSize.height();
526 if (aspectRatio < 1f) {
527 aspectRatio = 1 / aspectRatio;
528 }
529 final AspectRatioSelector.AspectRatio currentAspectRatio;
530 if (Math.abs(aspectRatio - 4f / 3f) <= 0.1f) {
531 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3;
532 } else if (Math.abs(aspectRatio - 16f / 9f) <= 0.1f) {
533 currentAspectRatio = AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9;
534 } else {
535 // TODO: Log error and not show dialog.
536 return null;
537 }
538
539 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
540 List<Size> pictureSizes = ResolutionUtil
541 .getDisplayableSizesFromSupported(sizes, true);
542
543 // This logic below finds the largest resolution for each aspect ratio.
544 // TODO: Move this somewhere that can be shared with SettingsActivity
545 int aspectRatio4x3Resolution = 0;
546 int aspectRatio16x9Resolution = 0;
547 Size largestSize4x3 = new Size(0, 0);
548 Size largestSize16x9 = new Size(0, 0);
549 for (Size size : pictureSizes) {
550 float pictureAspectRatio = (float) size.width() / (float) size.height();
551 pictureAspectRatio = pictureAspectRatio < 1 ?
552 1f / pictureAspectRatio : pictureAspectRatio;
553 int resolution = size.width() * size.height();
554 if (Math.abs(pictureAspectRatio - 4f / 3f) < 0.1f) {
555 if (resolution > aspectRatio4x3Resolution) {
556 aspectRatio4x3Resolution = resolution;
557 largestSize4x3 = size;
558 }
559 } else if (Math.abs(pictureAspectRatio - 16f / 9f) < 0.1f) {
560 if (resolution > aspectRatio16x9Resolution) {
561 aspectRatio16x9Resolution = resolution;
562 largestSize16x9 = size;
563 }
564 }
565 }
Doris Liudb8f9752014-05-12 15:25:13 -0700566
Doris Liu08b0cdd2014-05-13 19:19:55 -0700567 // Use the largest 4x3 and 16x9 sizes as candidates for picture size selection.
568 final Size size4x3ToSelect = largestSize4x3;
569 final Size size16x9ToSelect = largestSize16x9;
Doris Liudb8f9752014-05-12 15:25:13 -0700570
Doris Liudb8f9752014-05-12 15:25:13 -0700571 AspectRatioDialogCallback callback = new AspectRatioDialogCallback() {
Doris Liudb8f9752014-05-12 15:25:13 -0700572
573 @Override
574 public AspectRatioSelector.AspectRatio getCurrentAspectRatio() {
575 return currentAspectRatio;
576 }
577
578 @Override
Doris Liu08b0cdd2014-05-13 19:19:55 -0700579 public void onAspectRatioSelected(AspectRatioSelector.AspectRatio newAspectRatio,
580 Runnable dialogHandlingFinishedRunnable) {
Doris Liudb8f9752014-05-12 15:25:13 -0700581 if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_4x3) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700582 String largestSize4x3Text = SettingsUtil.sizeToSetting(size4x3ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700583 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
584 largestSize4x3Text);
585 } else if (newAspectRatio == AspectRatioSelector.AspectRatio.ASPECT_RATIO_16x9) {
Doris Liu08b0cdd2014-05-13 19:19:55 -0700586 String largestSize16x9Text = SettingsUtil.sizeToSetting(size16x9ToSelect);
Doris Liudb8f9752014-05-12 15:25:13 -0700587 mActivity.getSettingsManager().set(SettingsManager.SETTING_PICTURE_SIZE_BACK,
588 largestSize16x9Text);
589 }
590 mActivity.getSettingsManager().setBoolean(
591 SettingsManager.SETTING_USER_SELECTED_ASPECT_RATIO, true);
592 if (newAspectRatio != currentAspectRatio) {
Doris Liudb8f9752014-05-12 15:25:13 -0700593 stopPreview();
594 startPreview();
Doris Liu08b0cdd2014-05-13 19:19:55 -0700595 mUI.setRunnableForNextFrame(dialogHandlingFinishedRunnable);
596 } else {
597 mHandler.post(dialogHandlingFinishedRunnable);
Doris Liudb8f9752014-05-12 15:25:13 -0700598 }
599 }
600 };
601 return callback;
Doris Liu6a83d522013-07-02 12:03:32 -0700602 }
Michael Kolb8872c232013-01-29 10:33:22 -0800603
ztenghui7b265a62013-09-09 14:58:44 -0700604 @Override
Angus Kongdcccc512013-08-08 17:06:03 -0700605 public void onPreviewUIReady() {
Erin Dahlgrendc282e12013-11-12 09:39:08 -0800606 startPreview();
Angus Kongdcccc512013-08-08 17:06:03 -0700607 }
608
609 @Override
610 public void onPreviewUIDestroyed() {
611 if (mCameraDevice == null) {
612 return;
613 }
614 mCameraDevice.setPreviewTexture(null);
615 stopPreview();
616 }
617
Doris Liu1dfe7822013-12-12 00:02:08 -0800618 @Override
619 public void startPreCaptureAnimation() {
620 mAppController.startPreCaptureAnimation();
621 }
622
Michael Kolbd6954f32013-03-08 20:43:01 -0800623 private void onCameraOpened() {
Michael Kolbd6954f32013-03-08 20:43:01 -0800624 openCameraCommon();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800625 initializeControlByIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800626 }
627
Michael Kolbd6954f32013-03-08 20:43:01 -0800628 private void switchCamera() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800629 if (mPaused) {
630 return;
631 }
Doris Liu6c751642014-05-05 18:43:26 -0700632 cancelCountDown();
Doris Liu6809eb82014-05-21 13:16:59 -0700633
634 mAppController.freezeScreenUntilPreviewReady();
Erin Dahlgren357b7672013-11-20 17:38:14 -0800635 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolbd6954f32013-03-08 20:43:01 -0800636
Alan Newbergerd41766f2014-04-09 18:25:34 -0700637 Log.i(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId);
Angus Kongd4109bc2014-01-08 18:38:03 -0800638 closeCamera();
Michael Kolbd6954f32013-03-08 20:43:01 -0800639 mCameraId = mPendingSwitchCameraId;
Erin Dahlgren635a4b82013-11-25 15:21:18 -0800640 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -0800641 mActivity.getCameraProvider().requestCamera(mCameraId);
Michael Kolbd6954f32013-03-08 20:43:01 -0800642 mUI.clearFaces();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800643 if (mFocusManager != null) {
644 mFocusManager.removeMessages();
645 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800646
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700647 mMirror = isCameraFrontFacing();
Doris Liu29da2db2013-08-28 14:28:45 -0700648 mFocusManager.setMirror(mMirror);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700649 // Start switch camera animation. Post a message because
650 // onFrameAvailable from the old camera may already exist.
Doris Liu48239f42013-03-04 22:19:10 -0800651 }
652
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800653 private final ButtonManager.ButtonCallback mCameraCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100654 new ButtonManager.ButtonCallback() {
655 @Override
656 public void onStateChanged(int state) {
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800657 // At the time this callback is fired, the camera id
658 // has be set to the desired camera.
659
Angus Kong97e282a2014-03-04 18:44:49 -0800660 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Sascha Haeberlingde303232014-02-07 02:30:53 +0100661 return;
662 }
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800663 // If switching to back camera, and HDR+ is still on,
664 // switch back to gcam, otherwise handle callback normally.
665 SettingsManager settingsManager = mActivity.getSettingsManager();
666 if (settingsManager.isCameraBackFacing()) {
667 if (settingsManager.requestsReturnToHdrPlus()) {
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700668 switchToGcamCapture();
Erin Dahlgren1ba90e32014-03-03 12:05:57 -0800669 return;
670 }
671 }
672
Sascha Haeberlingde303232014-02-07 02:30:53 +0100673 mPendingSwitchCameraId = state;
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800674
Alan Newbergerd41766f2014-04-09 18:25:34 -0700675 Log.d(TAG, "Start to switch camera. cameraId=" + state);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100676 // We need to keep a preview frame for the animation before
677 // releasing the camera. This will trigger
678 // onPreviewTextureCopied.
679 // TODO: Need to animate the camera switch
680 switchCamera();
681 }
682 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800683
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800684 private final ButtonManager.ButtonCallback mHdrPlusCallback =
Sascha Haeberlingde303232014-02-07 02:30:53 +0100685 new ButtonManager.ButtonCallback() {
686 @Override
687 public void onStateChanged(int state) {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700688 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100689 if (GcamHelper.hasGcamCapture()) {
690 // Set the camera setting to default backfacing.
Sascha Haeberlingde303232014-02-07 02:30:53 +0100691 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_ID);
Erin Dahlgren4cdaf512014-03-19 12:48:30 -0700692 switchToGcamCapture();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100693 } else {
Doris Liu8ad8ad42014-03-27 14:43:31 -0700694 if (settingsManager.isHdrOn()) {
695 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
696 CameraUtil.SCENE_MODE_HDR);
697 } else {
698 settingsManager.set(SettingsManager.SETTING_SCENE_MODE,
699 Parameters.SCENE_MODE_AUTO);
700 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100701 updateParametersSceneMode();
Doris Liu8ad8ad42014-03-27 14:43:31 -0700702 mCameraDevice.setParameters(mParameters);
703 updateSceneMode();
Sascha Haeberlingde303232014-02-07 02:30:53 +0100704 }
Erin Dahlgrenba6994d2013-12-12 16:04:38 -0800705 }
Sascha Haeberlingde303232014-02-07 02:30:53 +0100706 };
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800707
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800708 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
709 @Override
710 public void onClick(View v) {
711 onCaptureCancelled();
712 }
713 };
714
715 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
716 @Override
717 public void onClick(View v) {
718 onCaptureDone();
719 }
720 };
721
722 private final View.OnClickListener mRetakeCallback = new View.OnClickListener() {
723 @Override
724 public void onClick(View v) {
Spike Sprague15690d02014-02-10 12:11:20 -0800725 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800726 onCaptureRetake();
727 }
728 };
729
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800730 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700731 public void hardResetSettings(SettingsManager settingsManager) {
Erin Dahlgrene1547932014-06-23 15:17:07 -0700732 // PhotoModule should hard reset HDR+ to off,
733 // and HDR to off if HDR+ is supported.
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700734 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR_PLUS, SettingsManager.VALUE_OFF);
Erin Dahlgrene1547932014-06-23 15:17:07 -0700735 if (GcamHelper.hasGcamCapture()) {
736 settingsManager.set(SettingsManager.SETTING_CAMERA_HDR, SettingsManager.VALUE_OFF);
737 }
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700738 }
739
740 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800741 public HardwareSpec getHardwareSpec() {
Erin Dahlgren49ab9222014-01-28 17:40:28 -0800742 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800743 }
744
745 @Override
746 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
747 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
748
749 bottomBarSpec.enableCamera = true;
750 bottomBarSpec.cameraCallback = mCameraCallback;
Angus Kong8c07fe92014-05-22 14:42:45 -0700751 bottomBarSpec.enableFlash = !SettingsManager.VALUE_ON
752 .equals(mAppController.getSettingsManager()
753 .get(SettingsManager.SETTING_CAMERA_HDR));
Erin Dahlgrena6587a12014-02-03 13:24:55 -0800754 bottomBarSpec.enableHdr = true;
755 bottomBarSpec.hdrCallback = mHdrPlusCallback;
Erin Dahlgrend5e51462014-02-07 12:38:57 -0800756 bottomBarSpec.enableGridLines = true;
Spike Sprague8f7425c2014-05-12 11:08:34 -0700757 if (mCameraCapabilities != null) {
758 bottomBarSpec.enableExposureCompensation = true;
759 bottomBarSpec.exposureCompensationSetCallback =
760 new CameraAppUI.BottomBarUISpec.ExposureCompensationSetCallback() {
761 @Override
762 public void setExposure(int value) {
763 setExposureCompensation(value);
764 }
765 };
766 bottomBarSpec.minExposureCompensation =
767 mCameraCapabilities.getMinExposureCompensation();
768 bottomBarSpec.maxExposureCompensation =
769 mCameraCapabilities.getMaxExposureCompensation();
770 bottomBarSpec.exposureCompensationStep =
771 mCameraCapabilities.getExposureCompensationStep();
772 }
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800773
Doris Liu6c751642014-05-05 18:43:26 -0700774 bottomBarSpec.enableSelfTimer = true;
Sascha Haeberling4333fac2014-05-20 16:28:11 -0700775 bottomBarSpec.showSelfTimer = true;
Doris Liu6c751642014-05-05 18:43:26 -0700776
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800777 if (isImageCaptureIntent()) {
778 bottomBarSpec.showCancel = true;
779 bottomBarSpec.cancelCallback = mCancelCallback;
780 bottomBarSpec.showDone = true;
781 bottomBarSpec.doneCallback = mDoneCallback;
782 bottomBarSpec.showRetake = true;
783 bottomBarSpec.retakeCallback = mRetakeCallback;
784 }
785
786 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800787 }
788
Michael Kolbd6954f32013-03-08 20:43:01 -0800789 // either open a new camera or switch cameras
790 private void openCameraCommon() {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800791 mUI.onCameraOpened(mParameters);
Angus Kong0fb819b2013-10-08 13:44:19 -0700792 if (mIsImageCaptureIntent) {
Erin Dahlgrene419b192013-12-03 13:10:27 -0800793 // Set hdr plus to default: off.
794 SettingsManager settingsManager = mActivity.getSettingsManager();
795 settingsManager.setDefault(SettingsManager.SETTING_CAMERA_HDR_PLUS);
Angus Kong0fb819b2013-10-08 13:44:19 -0700796 }
Michael Kolbd6954f32013-03-08 20:43:01 -0800797 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -0800798 }
799
ztenghui7b265a62013-09-09 14:58:44 -0700800 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800801 public void updatePreviewAspectRatio(float aspectRatio) {
802 mAppController.updatePreviewAspectRatio(aspectRatio);
803 }
804
Michael Kolb8872c232013-01-29 10:33:22 -0800805 private void resetExposureCompensation() {
Erin Dahlgren357b7672013-11-20 17:38:14 -0800806 SettingsManager settingsManager = mActivity.getSettingsManager();
807 if (settingsManager == null) {
808 Log.e(TAG, "Settings manager is null!");
809 return;
Michael Kolb8872c232013-01-29 10:33:22 -0800810 }
Spike Spragueabf54e22014-03-27 15:41:28 -0700811 settingsManager.setDefault(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE);
Michael Kolb8872c232013-01-29 10:33:22 -0800812 }
813
Michael Kolb8872c232013-01-29 10:33:22 -0800814 // Snapshots can only be taken after this is called. It should be called
815 // once only. We could have done these things in onCreate() but we want to
816 // make preview screen appear as soon as possible.
817 private void initializeFirstTime() {
Sascha Haeberling330dafb2013-10-10 19:00:41 -0700818 if (mFirstTimeInitialized || mPaused) {
819 return;
820 }
Michael Kolb8872c232013-01-29 10:33:22 -0800821
Michael Kolbd6954f32013-03-08 20:43:01 -0800822 mUI.initializeFirstTime();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800823
Angus Kong86d36312013-01-31 18:22:44 -0800824 // We set the listener only when both service and shutterbutton
825 // are initialized.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800826 getServices().getMemoryManager().addListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -0800827
Michael Kolb8872c232013-01-29 10:33:22 -0800828 mNamedImages = new NamedImages();
829
830 mFirstTimeInitialized = true;
831 addIdleHandler();
832
Spike Spraguee6374b72014-04-25 17:24:32 -0700833 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -0800834 }
835
Michael Kolbd6954f32013-03-08 20:43:01 -0800836 // If the activity is paused and resumed, this method will be called in
837 // onResume.
838 private void initializeSecondTime() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800839 getServices().getMemoryManager().addListener(this);
Michael Kolbd6954f32013-03-08 20:43:01 -0800840 mNamedImages = new NamedImages();
841 mUI.initializeSecondTime(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -0800842 }
843
Michael Kolb8872c232013-01-29 10:33:22 -0800844 private void addIdleHandler() {
845 MessageQueue queue = Looper.myQueue();
846 queue.addIdleHandler(new MessageQueue.IdleHandler() {
847 @Override
848 public boolean queueIdle() {
849 Storage.ensureOSXCompatible();
850 return false;
851 }
852 });
853 }
854
Michael Kolb8872c232013-01-29 10:33:22 -0800855 @Override
856 public void startFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800857 if (mFaceDetectionStarted) {
858 return;
859 }
Angus Kong88289042014-04-22 16:39:42 -0700860 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800861 mFaceDetectionStarted = true;
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700862 mUI.onStartFaceDetection(mDisplayOrientation, isCameraFrontFacing());
Angus Kong9e765522013-07-31 14:05:20 -0700863 mCameraDevice.setFaceDetectionCallback(mHandler, mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800864 mCameraDevice.startFaceDetection();
Andy Huibers10c58162014-03-29 14:06:54 -0700865 SessionStatsCollector.instance().faceScanActive(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800866 }
867 }
868
Michael Kolb8872c232013-01-29 10:33:22 -0800869 @Override
870 public void stopFaceDetection() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800871 if (!mFaceDetectionStarted) {
872 return;
873 }
Angus Kong88289042014-04-22 16:39:42 -0700874 if (mCameraCapabilities.getMaxNumOfFacesSupported() > 0) {
Michael Kolb8872c232013-01-29 10:33:22 -0800875 mFaceDetectionStarted = false;
Angus Kong9e765522013-07-31 14:05:20 -0700876 mCameraDevice.setFaceDetectionCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -0800877 mCameraDevice.stopFaceDetection();
Michael Kolbd6954f32013-03-08 20:43:01 -0800878 mUI.clearFaces();
Andy Huibers10c58162014-03-29 14:06:54 -0700879 SessionStatsCollector.instance().faceScanActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -0800880 }
881 }
882
Michael Kolb8872c232013-01-29 10:33:22 -0800883 private final class ShutterCallback
Angus Kong9ef99252013-07-18 18:04:19 -0700884 implements CameraShutterCallback {
Angus Kongdcb0ef12013-03-25 23:11:43 -0700885
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800886 private final boolean mNeedsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700887
Sascha Haeberling37f36112013-08-06 14:31:52 -0700888 public ShutterCallback(boolean needsAnimation) {
889 mNeedsAnimation = needsAnimation;
Angus Kongdcb0ef12013-03-25 23:11:43 -0700890 }
891
Michael Kolb8872c232013-01-29 10:33:22 -0800892 @Override
Angus Kong9ef99252013-07-18 18:04:19 -0700893 public void onShutter(CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800894 mShutterCallbackTime = System.currentTimeMillis();
895 mShutterLag = mShutterCallbackTime - mCaptureStartTime;
896 Log.v(TAG, "mShutterLag = " + mShutterLag + "ms");
Sascha Haeberling37f36112013-08-06 14:31:52 -0700897 if (mNeedsAnimation) {
898 mActivity.runOnUiThread(new Runnable() {
899 @Override
900 public void run() {
901 animateAfterShutter();
902 }
903 });
Angus Kongdcb0ef12013-03-25 23:11:43 -0700904 }
Michael Kolb8872c232013-01-29 10:33:22 -0800905 }
906 }
907
Angus Kong9ef99252013-07-18 18:04:19 -0700908 private final class PostViewPictureCallback
909 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800910 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800911 public void onPictureTaken(byte[] data, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800912 mPostViewPictureCallbackTime = System.currentTimeMillis();
913 Log.v(TAG, "mShutterToPostViewCallbackTime = "
914 + (mPostViewPictureCallbackTime - mShutterCallbackTime)
915 + "ms");
916 }
917 }
918
Angus Kong9ef99252013-07-18 18:04:19 -0700919 private final class RawPictureCallback
920 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800921 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800922 public void onPictureTaken(byte[] rawData, CameraProxy camera) {
Michael Kolb8872c232013-01-29 10:33:22 -0800923 mRawPictureCallbackTime = System.currentTimeMillis();
924 Log.v(TAG, "mShutterToRawCallbackTime = "
925 + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms");
926 }
927 }
928
Angus Kong454d63f2014-05-06 14:45:59 -0700929 private static class ResizeBundle {
930 byte[] jpegData;
931 float targetAspectRatio;
932 ExifInterface exif;
933 }
934
935 /**
936 * @return Cropped image if the target aspect ratio is larger than the jpeg
937 * aspect ratio on the long axis. The original jpeg otherwise.
938 */
939 private ResizeBundle cropJpegDataToAspectRatio(ResizeBundle dataBundle) {
940
941 final byte[] jpegData = dataBundle.jpegData;
942 final ExifInterface exif = dataBundle.exif;
943 float targetAspectRatio = dataBundle.targetAspectRatio;
944
945 Bitmap original = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
946 int originalWidth = original.getWidth();
947 int originalHeight = original.getHeight();
948 int newWidth;
949 int newHeight;
950
951 if (originalWidth > originalHeight) {
952 newHeight = (int) (originalWidth / targetAspectRatio);
953 newWidth = originalWidth;
954 } else {
955 newWidth = (int) (originalHeight / targetAspectRatio);
956 newHeight = originalHeight;
957 }
958 int xOffset = (originalWidth - newWidth)/2;
959 int yOffset = (originalHeight - newHeight)/2;
960
961 if (xOffset < 0 || yOffset < 0) {
962 return dataBundle;
963 }
964
965 Bitmap resized = Bitmap.createBitmap(original,xOffset,yOffset,newWidth, newHeight);
966 exif.setTagValue(ExifInterface.TAG_PIXEL_X_DIMENSION, new Integer(newWidth));
967 exif.setTagValue(ExifInterface.TAG_PIXEL_Y_DIMENSION, new Integer(newHeight));
968
969 ByteArrayOutputStream stream = new ByteArrayOutputStream();
970
971 resized.compress(Bitmap.CompressFormat.JPEG, 90, stream);
972 dataBundle.jpegData = stream.toByteArray();
973 return dataBundle;
974 }
975
Angus Kong9ef99252013-07-18 18:04:19 -0700976 private final class JpegPictureCallback
977 implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -0800978 Location mLocation;
979
980 public JpegPictureCallback(Location loc) {
981 mLocation = loc;
982 }
983
984 @Override
Angus Kong454d63f2014-05-06 14:45:59 -0700985 public void onPictureTaken(final byte[] originalJpegData, final CameraProxy camera) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700986 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800987 if (mPaused) {
988 return;
989 }
Doris Liu6432cd62013-06-13 17:20:31 -0700990 if (mIsImageCaptureIntent) {
991 stopPreview();
992 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700993 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -0700994 mUI.setSwipingEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800995 }
996
997 mJpegPictureCallbackTime = System.currentTimeMillis();
998 // If postview callback has arrived, the captured image is displayed
999 // in postview callback. If not, the captured image is displayed in
1000 // raw picture callback.
1001 if (mPostViewPictureCallbackTime != 0) {
1002 mShutterToPictureDisplayedTime =
1003 mPostViewPictureCallbackTime - mShutterCallbackTime;
1004 mPictureDisplayedToJpegCallbackTime =
1005 mJpegPictureCallbackTime - mPostViewPictureCallbackTime;
1006 } else {
1007 mShutterToPictureDisplayedTime =
1008 mRawPictureCallbackTime - mShutterCallbackTime;
1009 mPictureDisplayedToJpegCallbackTime =
1010 mJpegPictureCallbackTime - mRawPictureCallbackTime;
1011 }
1012 Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = "
1013 + mPictureDisplayedToJpegCallbackTime + "ms");
1014
Michael Kolb8872c232013-01-29 10:33:22 -08001015 mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden.
1016 if (!mIsImageCaptureIntent) {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001017 setupPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001018 }
1019
Angus Kong454d63f2014-05-06 14:45:59 -07001020 long now = System.currentTimeMillis();
1021 mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;
1022 Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms");
1023 mJpegPictureCallbackTime = 0;
1024
1025 final ExifInterface exif = Exif.getExif(originalJpegData);
1026
1027 if (mShouldResizeTo16x9) {
1028 final ResizeBundle dataBundle = new ResizeBundle();
1029 dataBundle.jpegData = originalJpegData;
1030 dataBundle.targetAspectRatio = ResolutionUtil.NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO;
1031 dataBundle.exif = exif;
1032 new AsyncTask<ResizeBundle, Void, ResizeBundle>() {
1033
1034 @Override
1035 protected ResizeBundle doInBackground(ResizeBundle... resizeBundles) {
1036 return cropJpegDataToAspectRatio(resizeBundles[0]);
1037 }
1038
1039 @Override
1040 protected void onPostExecute(ResizeBundle result) {
1041 saveFinalPhoto(result.jpegData, result.exif, camera);
1042 }
1043 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataBundle);
1044
1045 } else {
1046 saveFinalPhoto(originalJpegData, exif, camera);
1047 }
1048 }
1049
1050 void saveFinalPhoto(final byte[] jpegData, final ExifInterface exif, CameraProxy camera) {
1051
Doris Liu36e56fb2013-09-11 17:38:08 -07001052 int orientation = Exif.getOrientation(exif);
Andy Huibers10c58162014-03-29 14:06:54 -07001053
Andy Huiberse08bc042014-04-11 19:26:47 -07001054 float zoomValue = 0f;
Angus Kong88289042014-04-22 16:39:42 -07001055 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Andy Huiberse08bc042014-04-11 19:26:47 -07001056 int zoomIndex = mParameters.getZoom();
1057 List<Integer> zoomRatios = mParameters.getZoomRatios();
1058 if (zoomRatios != null && zoomIndex < zoomRatios.size()) {
1059 zoomValue = 0.01f * zoomRatios.get(zoomIndex);
1060 }
1061 }
Angus Kong454d63f2014-05-06 14:45:59 -07001062
Andy Huibers6b9743a2014-04-03 23:23:29 -07001063 boolean hdrOn = CameraUtil.SCENE_MODE_HDR.equals(mSceneMode);
Andy Huibers203abe52014-05-19 13:59:01 -07001064 String flashSetting =
1065 mActivity.getSettingsManager().get(SettingsManager.SETTING_FLASH_MODE);
1066 boolean gridLinesOn = mActivity.getSettingsManager().areGridLinesOn();
Andy Huibers10c58162014-03-29 14:06:54 -07001067 UsageStatistics.instance().photoCaptureDoneEvent(
1068 eventprotos.NavigationChange.Mode.PHOTO_CAPTURE,
1069 mNamedImages.mQueue.lastElement().title + ".jpg", exif,
Andy Huibers547d7c82014-05-20 23:03:18 -07001070 isCameraFrontFacing(), hdrOn, zoomValue, flashSetting, gridLinesOn,
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001071 (float) mTimerDuration, mShutterTouchCoordinate, mVolumeButtonClickedFlag);
1072 mShutterTouchCoordinate = null;
1073 mVolumeButtonClickedFlag = false;
Ruben Brunkd217ed02013-10-08 23:31:13 -07001074
Ruben Brunkd7488272013-10-10 18:45:53 -07001075 if (!mIsImageCaptureIntent) {
Michael Kolb8872c232013-01-29 10:33:22 -08001076 // Calculate the width and the height of the jpeg.
Angus Kong454d63f2014-05-06 14:45:59 -07001077 Integer exifWidth = exif.getTagIntValue(ExifInterface.TAG_PIXEL_X_DIMENSION);
1078 Integer exifHeight = exif.getTagIntValue(ExifInterface.TAG_PIXEL_Y_DIMENSION);
Michael Kolb8872c232013-01-29 10:33:22 -08001079 int width, height;
Angus Kong454d63f2014-05-06 14:45:59 -07001080 if (mShouldResizeTo16x9 && exifWidth != null && exifHeight != null) {
1081 width = exifWidth;
1082 height = exifHeight;
Michael Kolb8872c232013-01-29 10:33:22 -08001083 } else {
Angus Kong454d63f2014-05-06 14:45:59 -07001084 Size s;
1085 s = new Size(mParameters.getPictureSize());
1086 if ((mJpegRotation + orientation) % 180 == 0) {
1087 width = s.width();
1088 height = s.height();
1089 } else {
1090 width = s.height();
1091 height = s.width();
1092 }
Michael Kolb8872c232013-01-29 10:33:22 -08001093 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001094 NamedEntity name = mNamedImages.getNextNameEntity();
1095 String title = (name == null) ? null : name.title;
1096 long date = (name == null) ? -1 : name.date;
Ruben Brunkd7488272013-10-10 18:45:53 -07001097
1098 // Handle debug mode outputs
1099 if (mDebugUri != null) {
1100 // If using a debug uri, save jpeg there.
1101 saveToDebugUri(jpegData);
1102
1103 // Adjust the title of the debug image shown in mediastore.
1104 if (title != null) {
1105 title = DEBUG_IMAGE_PREFIX + title;
1106 }
1107 }
1108
Michael Kolb8872c232013-01-29 10:33:22 -08001109 if (title == null) {
1110 Log.e(TAG, "Unbalanced name/data pair");
1111 } else {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001112 if (date == -1) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001113 date = mCaptureStartTime;
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001114 }
Angus Kong0d00a892013-03-26 11:40:40 -07001115 if (mHeading >= 0) {
1116 // heading direction has been updated by the sensor.
1117 ExifTag directionRefTag = exif.buildTag(
1118 ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
1119 ExifInterface.GpsTrackRef.MAGNETIC_DIRECTION);
1120 ExifTag directionTag = exif.buildTag(
1121 ExifInterface.TAG_GPS_IMG_DIRECTION,
1122 new Rational(mHeading, 1));
1123 exif.setTag(directionRefTag);
1124 exif.setTag(directionTag);
1125 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001126 getServices().getMediaSaver().addImage(
Angus Kong86d36312013-01-31 18:22:44 -08001127 jpegData, title, date, mLocation, width, height,
Angus Kong0d00a892013-03-26 11:40:40 -07001128 orientation, exif, mOnMediaSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001129 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001130 // Animate capture with real jpeg data instead of a preview
1131 // frame.
Doris Liu29da2db2013-08-28 14:28:45 -07001132 mUI.animateCapture(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001133 } else {
1134 mJpegImageData = jpegData;
1135 if (!mQuickCapture) {
Doris Liu36e56fb2013-09-11 17:38:08 -07001136 mUI.showCapturedImageForReview(jpegData, orientation, mMirror);
Michael Kolb8872c232013-01-29 10:33:22 -08001137 } else {
Michael Kolbd6954f32013-03-08 20:43:01 -08001138 onCaptureDone();
Michael Kolb8872c232013-01-29 10:33:22 -08001139 }
1140 }
1141
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001142 // Send the taken photo to remote shutter listeners, if any are
1143 // registered.
1144 AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
1145 @Override
1146 public void run() {
1147 getServices().getRemoteShutterListener().onPictureTaken(jpegData);
1148 }
1149 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001150
Michael Kolb8872c232013-01-29 10:33:22 -08001151 // Check this in advance of each shot so we don't add to shutter
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001152 // latency. It's true that someone else could write to the SD card
1153 // in the mean time and fill it, but that could have happened
1154 // between the shutter press and saving the JPEG too.
Spike Spraguee6374b72014-04-25 17:24:32 -07001155 mActivity.updateStorageSpaceAndHint(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001156 }
1157 }
1158
Angus Kong9ef99252013-07-18 18:04:19 -07001159 private final class AutoFocusCallback implements CameraAFCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001160 @Override
Andy Huibers10c58162014-03-29 14:06:54 -07001161 public void onAutoFocus(boolean focused, CameraProxy camera) {
1162 SessionStatsCollector.instance().autofocusResult(focused);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001163 if (mPaused) {
1164 return;
1165 }
Michael Kolb8872c232013-01-29 10:33:22 -08001166
1167 mAutoFocusTime = System.currentTimeMillis() - mFocusStartTime;
Andy Huibers10c58162014-03-29 14:06:54 -07001168 Log.v(TAG, "mAutoFocusTime = " + mAutoFocusTime + "ms focused = "+focused);
Michael Kolb8872c232013-01-29 10:33:22 -08001169 setCameraState(IDLE);
Erin Dahlgren667630d2014-04-01 14:03:25 -07001170 mFocusManager.onAutoFocus(focused, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001171 }
1172 }
1173
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001174 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001175 private final class AutoFocusMoveCallback
Angus Kong9ef99252013-07-18 18:04:19 -07001176 implements CameraAFMoveCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001177 @Override
1178 public void onAutoFocusMoving(
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001179 boolean moving, CameraProxy camera) {
1180 mFocusManager.onAutoFocusMoving(moving);
Andy Huibers10c58162014-03-29 14:06:54 -07001181 SessionStatsCollector.instance().autofocusMoving(moving);
Michael Kolb8872c232013-01-29 10:33:22 -08001182 }
1183 }
1184
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001185 /**
1186 * This class is just a thread-safe queue for name,date holder objects.
1187 */
1188 public static class NamedImages {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001189 private final Vector<NamedEntity> mQueue;
Michael Kolb8872c232013-01-29 10:33:22 -08001190
1191 public NamedImages() {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001192 mQueue = new Vector<NamedEntity>();
Michael Kolb8872c232013-01-29 10:33:22 -08001193 }
1194
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001195 public void nameNewImage(long date) {
Michael Kolb8872c232013-01-29 10:33:22 -08001196 NamedEntity r = new NamedEntity();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001197 r.title = CameraUtil.createJpegName(date);
Michael Kolb8872c232013-01-29 10:33:22 -08001198 r.date = date;
1199 mQueue.add(r);
1200 }
1201
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001202 public NamedEntity getNextNameEntity() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001203 synchronized (mQueue) {
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001204 if (!mQueue.isEmpty()) {
1205 return mQueue.remove(0);
1206 }
Michael Kolb8872c232013-01-29 10:33:22 -08001207 }
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001208 return null;
Michael Kolb8872c232013-01-29 10:33:22 -08001209 }
1210
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001211 public static class NamedEntity {
1212 public String title;
1213 public long date;
Michael Kolb8872c232013-01-29 10:33:22 -08001214 }
1215 }
1216
1217 private void setCameraState(int state) {
1218 mCameraState = state;
1219 switch (state) {
Angus Kong62753ae2014-02-10 10:53:54 -08001220 case PREVIEW_STOPPED:
1221 case SNAPSHOT_IN_PROGRESS:
1222 case SWITCHING_CAMERA:
Doris Liuf55f3c42013-11-20 00:24:46 -08001223 // TODO: Tell app UI to disable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001224 break;
1225 case PhotoController.IDLE:
Doris Liuf55f3c42013-11-20 00:24:46 -08001226 // TODO: Tell app UI to enable swipe
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001227 break;
Michael Kolb8872c232013-01-29 10:33:22 -08001228 }
1229 }
1230
Sascha Haeberling37f36112013-08-06 14:31:52 -07001231 private void animateAfterShutter() {
Michael Kolb8872c232013-01-29 10:33:22 -08001232 // Only animate when in full screen capture mode
1233 // i.e. If monkey/a user swipes to the gallery during picture taking,
1234 // don't show animation
Doris Liuc2e9abd2013-06-19 14:20:51 -07001235 if (!mIsImageCaptureIntent) {
1236 mUI.animateFlash();
Doris Liuc2e9abd2013-06-19 14:20:51 -07001237 }
Michael Kolb8872c232013-01-29 10:33:22 -08001238 }
1239
1240 @Override
1241 public boolean capture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001242 // If we are already in the middle of taking a snapshot or the image
1243 // save request is full then ignore.
Michael Kolb8872c232013-01-29 10:33:22 -08001244 if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
Erin Dahlgren667630d2014-04-01 14:03:25 -07001245 || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001246 return false;
1247 }
1248 mCaptureStartTime = System.currentTimeMillis();
Sascha Haeberlingbca64bf2014-04-17 10:51:53 -07001249
Michael Kolb8872c232013-01-29 10:33:22 -08001250 mPostViewPictureCallbackTime = 0;
1251 mJpegImageData = null;
1252
Angus Kongb50b5cb2013-08-09 14:55:20 -07001253 final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR);
Michael Kolb8872c232013-01-29 10:33:22 -08001254
1255 if (animateBefore) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001256 animateAfterShutter();
Michael Kolb8872c232013-01-29 10:33:22 -08001257 }
1258
1259 // Set rotation and gps data.
Doris Liu3cf565c2013-02-15 10:55:37 -08001260 int orientation;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001261
Doris Liu3cf565c2013-02-15 10:55:37 -08001262 // We need to be consistent with the framework orientation (i.e. the
1263 // orientation of the UI.) when the auto-rotate screen setting is on.
1264 if (mActivity.isAutoRotateScreen()) {
1265 orientation = (360 - mDisplayRotation) % 360;
1266 } else {
1267 orientation = mOrientation;
1268 }
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +01001269 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
1270 mJpegRotation = CameraUtil.getJpegRotation(info, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001271 mParameters.setRotation(mJpegRotation);
Erin Dahlgrenc120b0f2013-11-19 10:53:49 -08001272 Location loc = mActivity.getLocationManager().getCurrentLocation();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001273 CameraUtil.setGpsParameters(mParameters, loc);
Michael Kolb8872c232013-01-29 10:33:22 -08001274 mCameraDevice.setParameters(mParameters);
1275
Sascha Haeberling88901942013-08-28 17:49:00 -07001276 // We don't want user to press the button again while taking a
1277 // multi-second HDR photo.
Erin Dahlgren667630d2014-04-01 14:03:25 -07001278 mAppController.setShutterEnabled(false);
Angus Kong9ef99252013-07-18 18:04:19 -07001279 mCameraDevice.takePicture(mHandler,
1280 new ShutterCallback(!animateBefore),
Angus Kongdcb0ef12013-03-25 23:11:43 -07001281 mRawPictureCallback, mPostViewPictureCallback,
Angus Kong9ef99252013-07-18 18:04:19 -07001282 new JpegPictureCallback(loc));
Michael Kolb8872c232013-01-29 10:33:22 -08001283
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001284 mNamedImages.nameNewImage(mCaptureStartTime);
Michael Kolb8872c232013-01-29 10:33:22 -08001285
1286 mFaceDetectionStarted = false;
1287 setCameraState(SNAPSHOT_IN_PROGRESS);
1288 return true;
1289 }
1290
1291 @Override
1292 public void setFocusParameters() {
1293 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1294 }
1295
Michael Kolbd6954f32013-03-08 20:43:01 -08001296 private void updateSceneMode() {
Michael Kolb8872c232013-01-29 10:33:22 -08001297 // If scene mode is set, we cannot set flash mode, white balance, and
1298 // focus mode, instead, we read it from driver
1299 if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
Doris Liu8ad8ad42014-03-27 14:43:31 -07001300 overrideCameraSettings(mParameters.getFlashMode(), mParameters.getFocusMode());
Michael Kolb8872c232013-01-29 10:33:22 -08001301 }
1302 }
1303
Doris Liu8ad8ad42014-03-27 14:43:31 -07001304 private void overrideCameraSettings(final String flashMode, final String focusMode) {
Erin Dahlgrene419b192013-12-03 13:10:27 -08001305 SettingsManager settingsManager = mActivity.getSettingsManager();
1306 settingsManager.set(SettingsManager.SETTING_FLASH_MODE, flashMode);
Erin Dahlgrene419b192013-12-03 13:10:27 -08001307 settingsManager.set(SettingsManager.SETTING_FOCUS_MODE, focusMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001308 }
1309
1310 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001311 public void onOrientationChanged(int orientation) {
1312 // We keep the last known orientation. So if the user first orient
1313 // the camera then point the camera to floor or sky, we still have
1314 // the correct orientation.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001315 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
1316 return;
1317 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001318 mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001319 }
1320
1321 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001322 public void onCameraAvailable(CameraProxy cameraProxy) {
1323 if (mPaused) {
1324 return;
1325 }
1326 mCameraDevice = cameraProxy;
1327
1328 initializeCapabilities();
1329
1330 // Reset zoom value index.
1331 mZoomValue = 0;
1332 if (mFocusManager == null) {
1333 initializeFocusManager();
1334 }
Angus Kong88289042014-04-22 16:39:42 -07001335 mFocusManager.setParameters(mInitialParams, mCameraCapabilities);
Angus Kong20fad242013-11-11 18:23:46 -08001336
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001337 // Do camera parameter dependent initialization.
Angus Kong20fad242013-11-11 18:23:46 -08001338 mParameters = mCameraDevice.getParameters();
1339 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001340 // Set a listener which updates camera parameters based
1341 // on changed settings.
1342 SettingsManager settingsManager = mActivity.getSettingsManager();
Erin Dahlgren1648c362014-01-06 15:06:04 -08001343 settingsManager.addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001344 mCameraPreviewParamsReady = true;
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001345
Angus Kong20fad242013-11-11 18:23:46 -08001346 startPreview();
1347
1348 onCameraOpened();
1349 }
1350
1351 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08001352 public void onCaptureCancelled() {
1353 mActivity.setResultEx(Activity.RESULT_CANCELED, new Intent());
1354 mActivity.finish();
Michael Kolb8872c232013-01-29 10:33:22 -08001355 }
1356
Michael Kolbd6954f32013-03-08 20:43:01 -08001357 @Override
1358 public void onCaptureRetake() {
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001359 if (mPaused) {
Michael Kolb8872c232013-01-29 10:33:22 -08001360 return;
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01001361 }
Michael Kolbd6954f32013-03-08 20:43:01 -08001362 mUI.hidePostCaptureAlert();
Spike Spragueb4a22222014-05-22 14:40:53 -07001363 mUI.hideIntentReviewImageView();
Michael Kolb8872c232013-01-29 10:33:22 -08001364 setupPreview();
1365 }
1366
Michael Kolbd6954f32013-03-08 20:43:01 -08001367 @Override
1368 public void onCaptureDone() {
Michael Kolb8872c232013-01-29 10:33:22 -08001369 if (mPaused) {
1370 return;
1371 }
1372
1373 byte[] data = mJpegImageData;
1374
1375 if (mCropValue == null) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001376 // First handle the no crop case -- just return the value. If the
Michael Kolb8872c232013-01-29 10:33:22 -08001377 // caller specifies a "save uri" then write the data to its
1378 // stream. Otherwise, pass back a scaled down version of the bitmap
1379 // directly in the extras.
1380 if (mSaveUri != null) {
1381 OutputStream outputStream = null;
1382 try {
1383 outputStream = mContentResolver.openOutputStream(mSaveUri);
1384 outputStream.write(data);
1385 outputStream.close();
1386
1387 mActivity.setResultEx(Activity.RESULT_OK);
1388 mActivity.finish();
1389 } catch (IOException ex) {
1390 // ignore exception
1391 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001392 CameraUtil.closeSilently(outputStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001393 }
1394 } else {
Angus Kong0d00a892013-03-26 11:40:40 -07001395 ExifInterface exif = Exif.getExif(data);
1396 int orientation = Exif.getOrientation(exif);
Angus Kongb50b5cb2013-08-09 14:55:20 -07001397 Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024);
1398 bitmap = CameraUtil.rotate(bitmap, orientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001399 mActivity.setResultEx(Activity.RESULT_OK,
1400 new Intent("inline-data").putExtra("data", bitmap));
1401 mActivity.finish();
1402 }
1403 } else {
1404 // Save the image to a temp file and invoke the cropper
1405 Uri tempUri = null;
1406 FileOutputStream tempStream = null;
1407 try {
1408 File path = mActivity.getFileStreamPath(sTempCropFilename);
1409 path.delete();
1410 tempStream = mActivity.openFileOutput(sTempCropFilename, 0);
1411 tempStream.write(data);
1412 tempStream.close();
1413 tempUri = Uri.fromFile(path);
1414 } catch (FileNotFoundException ex) {
1415 mActivity.setResultEx(Activity.RESULT_CANCELED);
1416 mActivity.finish();
1417 return;
1418 } catch (IOException ex) {
1419 mActivity.setResultEx(Activity.RESULT_CANCELED);
1420 mActivity.finish();
1421 return;
1422 } finally {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001423 CameraUtil.closeSilently(tempStream);
Michael Kolb8872c232013-01-29 10:33:22 -08001424 }
1425
1426 Bundle newExtras = new Bundle();
1427 if (mCropValue.equals("circle")) {
1428 newExtras.putString("circleCrop", "true");
1429 }
1430 if (mSaveUri != null) {
1431 newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
1432 } else {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001433 newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001434 }
1435 if (mActivity.isSecureCamera()) {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001436 newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true);
Michael Kolb8872c232013-01-29 10:33:22 -08001437 }
1438
Sascha Haeberling37f36112013-08-06 14:31:52 -07001439 // TODO: Share this constant.
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001440 final String CROP_ACTION = "com.android.camera.action.CROP";
1441 Intent cropIntent = new Intent(CROP_ACTION);
Michael Kolb8872c232013-01-29 10:33:22 -08001442
1443 cropIntent.setData(tempUri);
1444 cropIntent.putExtras(newExtras);
1445
1446 mActivity.startActivityForResult(cropIntent, REQUEST_CROP);
1447 }
1448 }
1449
Michael Kolb8872c232013-01-29 10:33:22 -08001450 @Override
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001451 public void onShutterCoordinate(TouchCoordinate coord) {
1452 mShutterTouchCoordinate = coord;
1453 }
1454
1455 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001456 public void onShutterButtonFocus(boolean pressed) {
Angus Kongeaaf56d2014-02-20 11:59:10 -08001457 // Do nothing. We don't support half-press to focus anymore.
Michael Kolb8872c232013-01-29 10:33:22 -08001458 }
1459
1460 @Override
1461 public void onShutterButtonClick() {
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001462 if (mPaused || (mCameraState == SWITCHING_CAMERA)
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001463 || (mCameraState == PREVIEW_STOPPED)) {
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001464 mVolumeButtonClickedFlag = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001465 return;
1466 }
Michael Kolb8872c232013-01-29 10:33:22 -08001467
1468 // Do not take the picture if there is not enough storage.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001469 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Michael Kolb8872c232013-01-29 10:33:22 -08001470 Log.i(TAG, "Not enough space or storage not ready. remaining="
Angus Kong2dcc0a92013-09-25 13:00:08 -07001471 + mActivity.getStorageSpaceBytes());
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001472 mVolumeButtonClickedFlag = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001473 return;
1474 }
Andy Huibersb7c7d9a2014-06-18 22:26:14 -07001475 Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
1476 " mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
Michael Kolb8872c232013-01-29 10:33:22 -08001477
Doris Liu6c751642014-05-05 18:43:26 -07001478 int countDownDuration = Integer.parseInt(mActivity.getSettingsManager()
1479 .get(SettingsManager.SETTING_COUNTDOWN_DURATION));
Andy Huibers547d7c82014-05-20 23:03:18 -07001480 mTimerDuration = countDownDuration;
Doris Liu6c751642014-05-05 18:43:26 -07001481 if (countDownDuration > 0) {
1482 // Start count down.
1483 mAppController.getCameraAppUI().transitionToCancel();
1484 mAppController.getCameraAppUI().hideModeOptions();
1485 mUI.startCountdown(countDownDuration);
1486 return;
1487 } else {
1488 focusAndCapture();
1489 }
1490 }
1491
1492 private void focusAndCapture() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001493 if (mSceneMode == CameraUtil.SCENE_MODE_HDR) {
Doris Liu6432cd62013-06-13 17:20:31 -07001494 mUI.setSwipingEnabled(false);
Doris Liu9cdfe002013-04-16 09:50:56 -07001495 }
Michael Kolb8872c232013-01-29 10:33:22 -08001496 // If the user wants to do a snapshot while the previous one is still
1497 // in progress, remember the fact and do it after we finish the previous
1498 // one and re-start the preview. Snapshot in progress also includes the
1499 // state that autofocus is focusing and a picture will be taken when
1500 // focus callback arrives.
Angus Kongeaaf56d2014-02-20 11:59:10 -08001501 if ((mFocusManager.isFocusingSnapOnFinish() || mCameraState == SNAPSHOT_IN_PROGRESS)) {
1502 if (!mIsImageCaptureIntent) {
1503 mSnapshotOnIdle = true;
1504 }
Michael Kolb8872c232013-01-29 10:33:22 -08001505 return;
1506 }
1507
Doris Liuf9e4f8f2013-12-04 18:04:22 -08001508 mSnapshotOnIdle = false;
Angus Kongeaaf56d2014-02-20 11:59:10 -08001509 mFocusManager.focusAndCapture();
Michael Kolb8872c232013-01-29 10:33:22 -08001510 }
1511
Doris Liu6c751642014-05-05 18:43:26 -07001512 @Override
1513 public void onRemainingSecondsChanged(int remainingSeconds) {
1514 mCountdownSoundPlayer.onRemainingSecondsChanged(remainingSeconds);
1515 }
1516
1517 @Override
1518 public void onCountDownFinished() {
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001519 if (mIsImageCaptureIntent) {
1520 mAppController.getCameraAppUI().transitionToIntentReviewLayout();
1521 } else {
1522 mAppController.getCameraAppUI().transitionToCapture();
1523 }
Doris Liu6c751642014-05-05 18:43:26 -07001524 mAppController.getCameraAppUI().showModeOptions();
1525 if (mPaused) {
1526 return;
1527 }
1528 focusAndCapture();
1529 }
1530
Andy Huibersdef975d2013-11-22 09:13:39 -08001531 private void onResumeTasks() {
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001532 if (mPaused) {
1533 return;
1534 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001535 Log.v(TAG, "Executing onResumeTasks.");
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001536 CameraProvider camProvider = mActivity.getCameraProvider();
1537 if (camProvider == null) {
1538 // No camera provider, the Activity is destroyed already.
1539 return;
1540 }
1541 camProvider.requestCamera(mCameraId);
Angus Kong20fad242013-11-11 18:23:46 -08001542
Michael Kolb8872c232013-01-29 10:33:22 -08001543 mJpegPictureCallbackTime = 0;
1544 mZoomValue = 0;
Angus Kong20fad242013-11-11 18:23:46 -08001545
1546 mOnResumeTime = SystemClock.uptimeMillis();
1547 checkDisplayRotation();
Michael Kolb8872c232013-01-29 10:33:22 -08001548
1549 // If first time initialization is not finished, put it in the
1550 // message queue.
1551 if (!mFirstTimeInitialized) {
Angus Kong13e87c42013-11-25 10:02:47 -08001552 mHandler.sendEmptyMessage(MSG_FIRST_TIME_INIT);
Michael Kolb8872c232013-01-29 10:33:22 -08001553 } else {
1554 initializeSecondTime();
1555 }
Michael Kolb8872c232013-01-29 10:33:22 -08001556
Angus Kong0d00a892013-03-26 11:40:40 -07001557 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1558 if (gsensor != null) {
1559 mSensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_NORMAL);
1560 }
1561
1562 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1563 if (msensor != null) {
1564 mSensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_NORMAL);
1565 }
Michael Kolb8872c232013-01-29 10:33:22 -08001566 }
1567
Angus Kongc4e66562013-11-22 23:03:21 -08001568 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001569 * @return Whether the currently active camera is front-facing.
1570 */
1571 private boolean isCameraFrontFacing() {
1572 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
1573 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
1574 }
1575
1576 /**
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001577 * The focus manager is the first UI related element to get initialized, and
1578 * it requires the RenderOverlay, so initialize it here
Angus Kongc4e66562013-11-22 23:03:21 -08001579 */
1580 private void initializeFocusManager() {
1581 // Create FocusManager object. startPreview needs it.
1582 // if mFocusManager not null, reuse it
1583 // otherwise create a new instance
1584 if (mFocusManager != null) {
1585 mFocusManager.removeMessages();
1586 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001587 mMirror = isCameraFrontFacing();
Angus Kongc4e66562013-11-22 23:03:21 -08001588 String[] defaultFocusModes = mActivity.getResources().getStringArray(
1589 R.array.pref_camera_focusmode_default_array);
Erin Dahlgren357b7672013-11-20 17:38:14 -08001590 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
1591 defaultFocusModes,
Angus Kongc4e66562013-11-22 23:03:21 -08001592 mInitialParams, this, mMirror,
Doris Liu482de022013-12-18 19:18:16 -08001593 mActivity.getMainLooper(), mUI.getFocusUI());
Kevin Gabayanfb333362014-06-02 14:48:20 -07001594 MotionManager motionManager = getServices().getMotionManager();
1595 if (motionManager != null) {
1596 motionManager.addListener(mFocusManager);
1597 }
Angus Kongc4e66562013-11-22 23:03:21 -08001598 }
Doris Liu482de022013-12-18 19:18:16 -08001599 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
Angus Kong20fad242013-11-11 18:23:46 -08001600 }
1601
1602 @Override
Angus Kongc4e66562013-11-22 23:03:21 -08001603 public void resume() {
1604 mPaused = false;
Doris Liu6c751642014-05-05 18:43:26 -07001605 mCountdownSoundPlayer.loadSounds();
Doris Liu482de022013-12-18 19:18:16 -08001606 if (mFocusManager != null) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001607 // If camera is not open when resume is called, focus manager will
1608 // not
Doris Liu15b99612013-12-21 11:32:28 -08001609 // be initialized yet, in which case it will start listening to
1610 // preview area size change later in the initialization.
Doris Liu482de022013-12-18 19:18:16 -08001611 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1612 }
Doris Liu6c751642014-05-05 18:43:26 -07001613 mAppController.addPreviewAreaSizeChangedListener(mUI);
Doris Liu59401042014-01-14 17:51:32 -08001614
Angus Kongc4e66562013-11-22 23:03:21 -08001615 // Add delay on resume from lock screen only, in order to to speed up
1616 // the onResume --> onPause --> onResume cycle from lock screen.
1617 // Don't do always because letting go of thread can cause delay.
1618 String action = mActivity.getIntent().getAction();
1619 if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(action)
1620 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)) {
1621 Log.v(TAG, "On resume, from lock screen.");
1622 // Note: onPauseAfterSuper() will delete this runnable, so we will
1623 // at most have 1 copy queued up.
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001624 mHandler.postDelayed(mResumeTaskRunnable, ON_RESUME_TASKS_DELAY_MSEC);
Angus Kongc4e66562013-11-22 23:03:21 -08001625 } else {
1626 Log.v(TAG, "On resume.");
1627 onResumeTasks();
1628 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08001629 getServices().getRemoteShutterListener().onModuleReady(this);
Andy Huibers10c58162014-03-29 14:06:54 -07001630 SessionStatsCollector.instance().sessionActive(true);
Angus Kongc4e66562013-11-22 23:03:21 -08001631 }
1632
1633 @Override
1634 public void pause() {
Michael Kolb8872c232013-01-29 10:33:22 -08001635 mPaused = true;
Angus Kong0b9eb5b2014-04-30 15:03:33 -07001636 mHandler.removeCallbacks(mResumeTaskRunnable);
Sascha Haeberling1bf08892014-06-18 09:38:06 -07001637 getServices().getRemoteShutterListener().onModuleExit();
Andy Huibers10c58162014-03-29 14:06:54 -07001638 SessionStatsCollector.instance().sessionActive(false);
Spike Spragueabf54e22014-03-27 15:41:28 -07001639
Angus Kong0d00a892013-03-26 11:40:40 -07001640 Sensor gsensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
1641 if (gsensor != null) {
1642 mSensorManager.unregisterListener(this, gsensor);
1643 }
1644
1645 Sensor msensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
1646 if (msensor != null) {
1647 mSensorManager.unregisterListener(this, msensor);
1648 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001649
Michael Kolb8872c232013-01-29 10:33:22 -08001650 // Reset the focus first. Camera CTS does not guarantee that
1651 // cancelAutoFocus is allowed after preview stops.
1652 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
1653 mCameraDevice.cancelAutoFocus();
1654 }
Andy Huibersdef975d2013-11-22 09:13:39 -08001655
Erin Dahlgrenb09b53e2013-11-06 11:57:51 -08001656 // If the camera has not been opened asynchronously yet,
1657 // and startPreview hasn't been called, then this is a no-op.
1658 // (e.g. onResume -> onPause -> onResume).
Michael Kolb8872c232013-01-29 10:33:22 -08001659 stopPreview();
Doris Liu6c751642014-05-05 18:43:26 -07001660 cancelCountDown();
1661 mCountdownSoundPlayer.release();
Michael Kolb8872c232013-01-29 10:33:22 -08001662
Angus Kongce5480e2013-01-29 17:43:48 -08001663 mNamedImages = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001664 // If we are in an image capture intent and has taken
1665 // a picture, we just clear it in onPause.
1666 mJpegImageData = null;
1667
Angus Kongdcccc512013-08-08 17:06:03 -07001668 // Remove the messages and runnables in the queue.
1669 mHandler.removeCallbacksAndMessages(null);
Michael Kolb8872c232013-01-29 10:33:22 -08001670
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001671 closeCamera();
Angus Kong13e87c42013-11-25 10:02:47 -08001672 mActivity.enableKeepScreenOn(false);
Michael Kolbd6954f32013-03-08 20:43:01 -08001673 mUI.onPause();
1674
Michael Kolb8872c232013-01-29 10:33:22 -08001675 mPendingSwitchCameraId = -1;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001676 if (mFocusManager != null) {
1677 mFocusManager.removeMessages();
Angus Kong86d36312013-01-31 18:22:44 -08001678 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001679 getServices().getMemoryManager().removeListener(this);
Doris Liu482de022013-12-18 19:18:16 -08001680 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
Doris Liu6c751642014-05-05 18:43:26 -07001681 mAppController.removePreviewAreaSizeChangedListener(mUI);
Erin Dahlgren1648c362014-01-06 15:06:04 -08001682
1683 SettingsManager settingsManager = mActivity.getSettingsManager();
1684 settingsManager.removeListener(this);
Michael Kolb8872c232013-01-29 10:33:22 -08001685 }
1686
Angus Kong20fad242013-11-11 18:23:46 -08001687 @Override
1688 public void destroy() {
1689 // TODO: implement this.
1690 }
1691
1692 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001693 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001694 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001695 }
1696
1697 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001698 public void updateCameraOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001699 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -07001700 setDisplayOrientation();
1701 }
1702 }
1703
Michael Kolb8872c232013-01-29 10:33:22 -08001704 private boolean canTakePicture() {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001705 return isCameraIdle()
1706 && (mActivity.getStorageSpaceBytes() > Storage.LOW_STORAGE_THRESHOLD_BYTES);
Michael Kolb8872c232013-01-29 10:33:22 -08001707 }
1708
1709 @Override
1710 public void autoFocus() {
Andy Huibers10c58162014-03-29 14:06:54 -07001711 Log.v(TAG,"Starting auto focus");
Michael Kolb8872c232013-01-29 10:33:22 -08001712 mFocusStartTime = System.currentTimeMillis();
Angus Kong9ef99252013-07-18 18:04:19 -07001713 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
Andy Huibers10c58162014-03-29 14:06:54 -07001714 SessionStatsCollector.instance().autofocusManualTrigger();
Michael Kolb8872c232013-01-29 10:33:22 -08001715 setCameraState(FOCUSING);
1716 }
1717
1718 @Override
1719 public void cancelAutoFocus() {
1720 mCameraDevice.cancelAutoFocus();
1721 setCameraState(IDLE);
1722 setCameraParameters(UPDATE_PARAM_PREFERENCE);
1723 }
1724
Michael Kolb8872c232013-01-29 10:33:22 -08001725 @Override
1726 public void onSingleTapUp(View view, int x, int y) {
Doris Liu482de022013-12-18 19:18:16 -08001727 if (mPaused || mCameraDevice == null || !mFirstTimeInitialized
1728 || mCameraState == SNAPSHOT_IN_PROGRESS
1729 || mCameraState == SWITCHING_CAMERA
1730 || mCameraState == PREVIEW_STOPPED) {
1731 return;
1732 }
1733
1734 // Check if metering area or focus area is supported.
Doris Liu15b99612013-12-21 11:32:28 -08001735 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
1736 return;
1737 }
Doris Liu482de022013-12-18 19:18:16 -08001738 mFocusManager.onSingleTapUp(x, y);
Michael Kolb8872c232013-01-29 10:33:22 -08001739 }
1740
1741 @Override
1742 public boolean onBackPressed() {
Michael Kolbd6954f32013-03-08 20:43:01 -08001743 return mUI.onBackPressed();
Michael Kolb8872c232013-01-29 10:33:22 -08001744 }
1745
1746 @Override
1747 public boolean onKeyDown(int keyCode, KeyEvent event) {
1748 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001749 case KeyEvent.KEYCODE_VOLUME_UP:
1750 case KeyEvent.KEYCODE_VOLUME_DOWN:
1751 case KeyEvent.KEYCODE_FOCUS:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001752 if (/* TODO: mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1753 !mActivity.getCameraAppUI().isInIntentReview()) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001754 if (event.getRepeatCount() == 0) {
1755 onShutterButtonFocus(true);
1756 }
1757 return true;
1758 }
1759 return false;
1760 case KeyEvent.KEYCODE_CAMERA:
1761 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1762 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -08001763 }
1764 return true;
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001765 case KeyEvent.KEYCODE_DPAD_CENTER:
1766 // If we get a dpad center event without any focused view, move
1767 // the focus to the shutter button and press it.
1768 if (mFirstTimeInitialized && event.getRepeatCount() == 0) {
1769 // Start auto-focus immediately to reduce shutter lag. After
1770 // the shutter button gets the focus, onShutterButtonFocus()
1771 // will be called again but it is fine.
1772 onShutterButtonFocus(true);
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001773 }
1774 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001775 }
1776 return false;
1777 }
1778
1779 @Override
1780 public boolean onKeyUp(int keyCode, KeyEvent event) {
1781 switch (keyCode) {
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001782 case KeyEvent.KEYCODE_VOLUME_UP:
1783 case KeyEvent.KEYCODE_VOLUME_DOWN:
Spike Sprague9f3d01d2014-06-30 14:52:57 -07001784 if (/* mActivity.isInCameraApp() && */mFirstTimeInitialized &&
1785 !mActivity.getCameraAppUI().isInIntentReview()) {
1786 if (mUI.isCountingDown()) {
1787 cancelCountDown();
1788 } else {
1789 mVolumeButtonClickedFlag = true;
1790 onShutterButtonClick();
1791 }
Dan Aminzade92ae10e2013-08-13 14:44:25 -07001792 return true;
1793 }
1794 return false;
1795 case KeyEvent.KEYCODE_FOCUS:
1796 if (mFirstTimeInitialized) {
1797 onShutterButtonFocus(false);
1798 }
Michael Kolb8872c232013-01-29 10:33:22 -08001799 return true;
Michael Kolb8872c232013-01-29 10:33:22 -08001800 }
1801 return false;
1802 }
1803
Michael Kolb8872c232013-01-29 10:33:22 -08001804 private void closeCamera() {
1805 if (mCameraDevice != null) {
Angus Kong97e282a2014-03-04 18:44:49 -08001806 stopFaceDetection();
Michael Kolb8872c232013-01-29 10:33:22 -08001807 mCameraDevice.setZoomChangeListener(null);
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001808 mCameraDevice.setFaceDetectionCallback(null, null);
Angus Kong2bca2102014-03-11 16:27:30 -07001809 mCameraDevice.setErrorCallback(null, null);
Ruben Brunk59147832013-11-05 15:53:28 -08001810
Michael Kolb8872c232013-01-29 10:33:22 -08001811 mFaceDetectionStarted = false;
Angus Kong20fad242013-11-11 18:23:46 -08001812 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Michael Kolb8872c232013-01-29 10:33:22 -08001813 mCameraDevice = null;
1814 setCameraState(PREVIEW_STOPPED);
1815 mFocusManager.onCameraReleased();
1816 }
1817 }
1818
1819 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07001820 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
1821 mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -07001822 mCameraDisplayOrientation = mDisplayOrientation;
Michael Kolbd6954f32013-03-08 20:43:01 -08001823 mUI.setDisplayOrientation(mDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -08001824 if (mFocusManager != null) {
1825 mFocusManager.setDisplayOrientation(mDisplayOrientation);
1826 }
Doris Liu6432cd62013-06-13 17:20:31 -07001827 // Change the camera display orientation
1828 if (mCameraDevice != null) {
1829 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
1830 }
Michael Kolb8872c232013-01-29 10:33:22 -08001831 }
1832
Sascha Haeberlingddef7792013-08-13 14:41:10 -07001833 /** Only called by UI thread. */
Michael Kolb8872c232013-01-29 10:33:22 -08001834 private void setupPreview() {
1835 mFocusManager.resetTouchFocus();
1836 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001837 }
1838
Angus Kong20fad242013-11-11 18:23:46 -08001839 /**
1840 * Returns whether we can/should start the preview or not.
1841 */
1842 private boolean checkPreviewPreconditions() {
1843 if (mPaused) {
1844 return false;
Angus Kongdcccc512013-08-08 17:06:03 -07001845 }
Michael Kolb8872c232013-01-29 10:33:22 -08001846
Angus Kong20fad242013-11-11 18:23:46 -08001847 if (mCameraDevice == null) {
1848 Log.w(TAG, "startPreview: camera device not ready yet.");
1849 return false;
1850 }
1851
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001852 SurfaceTexture st = mActivity.getCameraAppUI().getSurfaceTexture();
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001853 if (st == null) {
1854 Log.w(TAG, "startPreview: surfaceTexture is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001855 return false;
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001856 }
1857
1858 if (!mCameraPreviewParamsReady) {
1859 Log.w(TAG, "startPreview: parameters for preview is not ready.");
Angus Kong20fad242013-11-11 18:23:46 -08001860 return false;
1861 }
1862 return true;
1863 }
1864
1865 /**
1866 * The start/stop preview should only run on the UI thread.
1867 */
1868 private void startPreview() {
1869 if (!checkPreviewPreconditions()) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001870 return;
1871 }
Angus Kong20fad242013-11-11 18:23:46 -08001872
Angus Kong2bca2102014-03-11 16:27:30 -07001873 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001874 setDisplayOrientation();
1875
1876 if (!mSnapshotOnIdle) {
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001877 // If the focus mode is continuous autofocus, call cancelAutoFocus
1878 // to resume it because it may have been paused by autoFocus call.
Erin Dahlgren357b7672013-11-20 17:38:14 -08001879 String focusMode = mFocusManager.getFocusMode();
1880 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001881 mCameraDevice.cancelAutoFocus();
Michael Kolb8872c232013-01-29 10:33:22 -08001882 }
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001883 mFocusManager.setAeAwbLock(false); // Unlock AE and AWB.
1884 }
1885 setCameraParameters(UPDATE_PARAM_ALL);
Erin Dahlgrend8de0772014-02-03 10:12:27 -08001886 mCameraDevice.setPreviewTexture(mActivity.getCameraAppUI().getSurfaceTexture());
Michael Kolb8872c232013-01-29 10:33:22 -08001887
Alan Newbergerd41766f2014-04-09 18:25:34 -07001888 Log.i(TAG, "startPreview");
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001889 mCameraDevice.startPreview();
Angus Kong20fad242013-11-11 18:23:46 -08001890
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001891 mFocusManager.onPreviewStarted();
1892 onPreviewStarted();
Andy Huibers10c58162014-03-29 14:06:54 -07001893 SessionStatsCollector.instance().previewActive(true);
Erin Dahlgrendc282e12013-11-12 09:39:08 -08001894 if (mSnapshotOnIdle) {
1895 mHandler.post(mDoSnapRunnable);
Michael Kolb8872c232013-01-29 10:33:22 -08001896 }
1897 }
1898
Michael Kolbd6954f32013-03-08 20:43:01 -08001899 @Override
1900 public void stopPreview() {
Michael Kolb8872c232013-01-29 10:33:22 -08001901 if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001902 Log.i(TAG, "stopPreview");
Michael Kolb8872c232013-01-29 10:33:22 -08001903 mCameraDevice.stopPreview();
1904 mFaceDetectionStarted = false;
1905 }
1906 setCameraState(PREVIEW_STOPPED);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001907 if (mFocusManager != null) {
1908 mFocusManager.onPreviewStopped();
1909 }
Andy Huibers10c58162014-03-29 14:06:54 -07001910 SessionStatsCollector.instance().previewActive(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001911 }
1912
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001913 @Override
Erin Dahlgren1648c362014-01-06 15:06:04 -08001914 public void onSettingChanged(SettingsManager settingsManager, int id) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001915 switch (id) {
1916 case SettingsManager.SETTING_FLASH_MODE: {
1917 updateParametersFlashMode();
1918 break;
1919 }
Angus Kong8c07fe92014-05-22 14:42:45 -07001920 case SettingsManager.SETTING_CAMERA_HDR: {
Angus Kong3699c412014-05-23 15:31:09 -07001921 String val = settingsManager.get(SettingsManager.SETTING_CAMERA_HDR);
Angus Kong8c07fe92014-05-22 14:42:45 -07001922 if (SettingsManager.VALUE_ON.equals(val)) {
Angus Kong3699c412014-05-23 15:31:09 -07001923 // HDR is on.
Angus Kong8c07fe92014-05-22 14:42:45 -07001924 mAppController.getButtonManager().disableButton(ButtonManager.BUTTON_FLASH);
Angus Kong3699c412014-05-23 15:31:09 -07001925 mFlashModeBeforeSceneMode = settingsManager.get(SettingsManager
1926 .SETTING_FLASH_MODE);
Angus Kong8c07fe92014-05-22 14:42:45 -07001927 } else {
Angus Kong3699c412014-05-23 15:31:09 -07001928 if (mFlashModeBeforeSceneMode != null) {
1929 settingsManager.set(SettingsManager.SETTING_FLASH_MODE,
1930 mFlashModeBeforeSceneMode);
1931 updateParametersFlashMode();
1932 mFlashModeBeforeSceneMode = null;
1933 }
Angus Kong8c07fe92014-05-22 14:42:45 -07001934 mAppController.getButtonManager().enableButton(ButtonManager.BUTTON_FLASH);
1935 }
1936 break;
1937 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001938 default: {
1939 // Do nothing.
1940 }
1941 }
Erin Dahlgren1648c362014-01-06 15:06:04 -08001942
1943 if (mCameraDevice != null) {
1944 mCameraDevice.setParameters(mParameters);
1945 }
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08001946 }
1947
Michael Kolb8872c232013-01-29 10:33:22 -08001948 private void updateCameraParametersInitialize() {
1949 // Reset preview frame rate to the maximum because it may be lowered by
1950 // video camera application.
Angus Kong88289042014-04-22 16:39:42 -07001951 int[] fpsRange = CameraUtil.getPhotoPreviewFpsRange(mCameraCapabilities);
ztenghui16a35202013-09-23 11:35:36 -07001952 if (fpsRange != null && fpsRange.length > 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001953 mParameters.setPreviewFpsRange(
1954 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1955 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
Michael Kolb8872c232013-01-29 10:33:22 -08001956 }
1957
Angus Kongb50b5cb2013-08-09 14:55:20 -07001958 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE);
Michael Kolb8872c232013-01-29 10:33:22 -08001959
1960 // Disable video stabilization. Convenience methods not available in API
1961 // level <= 14
1962 String vstabSupported = mParameters.get("video-stabilization-supported");
1963 if ("true".equals(vstabSupported)) {
1964 mParameters.set("video-stabilization", "false");
1965 }
1966 }
1967
1968 private void updateCameraParametersZoom() {
1969 // Set zoom.
Angus Kong88289042014-04-22 16:39:42 -07001970 if (mCameraCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
Michael Kolb8872c232013-01-29 10:33:22 -08001971 mParameters.setZoom(mZoomValue);
1972 }
1973 }
1974
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001975 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001976 private void setAutoExposureLockIfSupported() {
1977 if (mAeLockSupported) {
1978 mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock());
1979 }
1980 }
1981
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001982 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08001983 private void setAutoWhiteBalanceLockIfSupported() {
1984 if (mAwbLockSupported) {
1985 mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock());
1986 }
1987 }
1988
Michael Kolb8872c232013-01-29 10:33:22 -08001989 private void setFocusAreasIfSupported() {
1990 if (mFocusAreaSupported) {
1991 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1992 }
1993 }
1994
Michael Kolb8872c232013-01-29 10:33:22 -08001995 private void setMeteringAreasIfSupported() {
1996 if (mMeteringAreaSupported) {
Michael Kolb8872c232013-01-29 10:33:22 -08001997 mParameters.setMeteringAreas(mFocusManager.getMeteringAreas());
1998 }
1999 }
2000
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002001 private void updateCameraParametersPreference() {
Michael Kolb8872c232013-01-29 10:33:22 -08002002 setAutoExposureLockIfSupported();
2003 setAutoWhiteBalanceLockIfSupported();
2004 setFocusAreasIfSupported();
2005 setMeteringAreasIfSupported();
2006
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002007 // Initialize focus mode.
Michael Kolbd3253f22013-07-12 11:36:47 -07002008 mFocusManager.overrideFocusMode(null);
2009 mParameters.setFocusMode(mFocusManager.getFocusMode());
Andy Huibers10c58162014-03-29 14:06:54 -07002010 SessionStatsCollector.instance().autofocusActive(
2011 mFocusManager.getFocusMode() == CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolbd3253f22013-07-12 11:36:47 -07002012
Michael Kolb8872c232013-01-29 10:33:22 -08002013 // Set picture size.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002014 updateParametersPictureSize();
2015
2016 // Set JPEG quality.
2017 updateParametersPictureQuality();
2018
2019 // For the following settings, we need to check if the settings are
2020 // still supported by latest driver, if not, ignore the settings.
2021
2022 // Set exposure compensation
2023 updateParametersExposureCompensation();
2024
2025 // Set the scene mode: also sets flash and white balance.
2026 updateParametersSceneMode();
2027
2028 if (mContinuousFocusSupported && ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK) {
2029 updateAutoFocusMoveCallback();
2030 }
2031 }
2032
2033 private void updateParametersPictureSize() {
2034 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002035 String pictureSize = settingsManager
2036 .get(isCameraFrontFacing() ? SettingsManager.SETTING_PICTURE_SIZE_FRONT
2037 : SettingsManager.SETTING_PICTURE_SIZE_BACK);
Sascha Haeberling3b0ab892014-01-29 20:54:39 +01002038
Angus Kong63424662014-04-23 10:47:47 -07002039 List<Size> supported = Size.buildListFromCameraSizes(mParameters.getSupportedPictureSizes());
Andy Huibers90c7ad52014-05-20 22:13:54 -07002040 CameraPictureSizesCacher.updateSizesForCamera(mAppController.getAndroidContext(),
2041 mCameraDevice.getCameraId(), supported);
Sascha Haeberling6ccec202014-03-11 09:44:34 -07002042 SettingsUtil.setCameraPictureSize(pictureSize, supported, mParameters,
2043 mCameraDevice.getCameraId());
Angus Kong454d63f2014-05-06 14:45:59 -07002044
2045 Size size = SettingsUtil.getPhotoSize(pictureSize, supported,
2046 mCameraDevice.getCameraId());
2047 if (ApiHelper.IS_NEXUS_5) {
2048 if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(pictureSize)) {
2049 mShouldResizeTo16x9 = true;
2050 } else {
2051 mShouldResizeTo16x9 = false;
2052 }
2053 }
Michael Kolb8872c232013-01-29 10:33:22 -08002054
2055 // Set a preview size that is closest to the viewfinder height and has
2056 // the right aspect ratio.
Angus Kong63424662014-04-23 10:47:47 -07002057 List<Size> sizes = Size.buildListFromCameraSizes(mParameters.getSupportedPreviewSizes());
Angus Kongb50b5cb2013-08-09 14:55:20 -07002058 Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
Angus Kong00b7b102014-04-24 15:46:52 -07002059 (double) size.width() / size.height());
Angus Kong63424662014-04-23 10:47:47 -07002060 Size original = new Size(mParameters.getPreviewSize());
Michael Kolb8872c232013-01-29 10:33:22 -08002061 if (!original.equals(optimalSize)) {
Angus Kong00b7b102014-04-24 15:46:52 -07002062 mParameters.setPreviewSize(optimalSize.width(), optimalSize.height());
Doris Liu6432cd62013-06-13 17:20:31 -07002063
Michael Kolb8872c232013-01-29 10:33:22 -08002064 // Zoom related settings will be changed for different preview
2065 // sizes, so set and read the parameters to get latest values
Michael Kolb4a4d4ef2013-05-30 08:35:26 -07002066 if (mHandler.getLooper() == Looper.myLooper()) {
2067 // On UI thread only, not when camera starts up
2068 setupPreview();
2069 } else {
2070 mCameraDevice.setParameters(mParameters);
2071 }
Michael Kolb8872c232013-01-29 10:33:22 -08002072 mParameters = mCameraDevice.getParameters();
2073 }
Doris Liu95405742013-11-05 15:25:26 -08002074
Angus Kong00b7b102014-04-24 15:46:52 -07002075 if (optimalSize.width() != 0 && optimalSize.height() != 0) {
2076 mUI.updatePreviewAspectRatio((float) optimalSize.width()
2077 / (float) optimalSize.height());
Doris Liu95405742013-11-05 15:25:26 -08002078 }
Angus Kong00b7b102014-04-24 15:46:52 -07002079 Log.i(TAG, "Preview size is " + optimalSize);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002080 }
Michael Kolb8872c232013-01-29 10:33:22 -08002081
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002082 private void updateParametersPictureQuality() {
Michael Kolb8872c232013-01-29 10:33:22 -08002083 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
2084 CameraProfile.QUALITY_HIGH);
2085 mParameters.setJpegQuality(jpegQuality);
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002086 }
Michael Kolb8872c232013-01-29 10:33:22 -08002087
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002088 private void updateParametersExposureCompensation() {
2089 SettingsManager settingsManager = mActivity.getSettingsManager();
Spike Spragueabf54e22014-03-27 15:41:28 -07002090 if (settingsManager.getBoolean(SettingsManager.SETTING_EXPOSURE_COMPENSATION_ENABLED)) {
2091 int value = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE));
Angus Kong88289042014-04-22 16:39:42 -07002092 int max = mCameraCapabilities.getMaxExposureCompensation();
2093 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002094 if (value >= min && value <= max) {
2095 mParameters.setExposureCompensation(value);
2096 } else {
2097 Log.w(TAG, "invalid exposure range: " + value);
2098 }
Michael Kolb8872c232013-01-29 10:33:22 -08002099 } else {
Spike Spragueabf54e22014-03-27 15:41:28 -07002100 // If exposure compensation is not enabled, reset the exposure compensation value.
2101 setExposureCompensation(0);
Michael Kolb8872c232013-01-29 10:33:22 -08002102 }
Spike Spragueabf54e22014-03-27 15:41:28 -07002103
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002104 }
2105
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002106 private void updateParametersSceneMode() {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002107 SettingsManager settingsManager = mActivity.getSettingsManager();
2108
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002109 mSceneMode = settingsManager.get(SettingsManager.SETTING_SCENE_MODE);
Angus Kong88289042014-04-22 16:39:42 -07002110 if (mCameraCapabilities
2111 .supports(mCameraCapabilities.getStringifier().sceneModeFromString(mSceneMode))) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002112 if (!mParameters.getSceneMode().equals(mSceneMode)) {
2113 mParameters.setSceneMode(mSceneMode);
2114
2115 // Setting scene mode will change the settings of flash mode,
2116 // white balance, and focus mode. Here we read back the
2117 // parameters, so we can know those settings.
2118 mCameraDevice.setParameters(mParameters);
2119 mParameters = mCameraDevice.getParameters();
2120 }
2121 } else {
2122 mSceneMode = mParameters.getSceneMode();
2123 if (mSceneMode == null) {
2124 mSceneMode = Parameters.SCENE_MODE_AUTO;
2125 }
2126 }
2127
Michael Kolb8872c232013-01-29 10:33:22 -08002128 if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) {
2129 // Set flash mode.
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002130 updateParametersFlashMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002131
Michael Kolb8872c232013-01-29 10:33:22 -08002132 // Set focus mode.
2133 mFocusManager.overrideFocusMode(null);
2134 mParameters.setFocusMode(mFocusManager.getFocusMode());
2135 } else {
2136 mFocusManager.overrideFocusMode(mParameters.getFocusMode());
2137 }
Michael Kolb8872c232013-01-29 10:33:22 -08002138 }
2139
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002140 private void updateParametersFlashMode() {
2141 SettingsManager settingsManager = mActivity.getSettingsManager();
2142
2143 String flashMode = settingsManager.get(SettingsManager.SETTING_FLASH_MODE);
Angus Kong88289042014-04-22 16:39:42 -07002144 if (mCameraCapabilities
2145 .supports(mCameraCapabilities.getStringifier().flashModeFromString(flashMode))) {
Erin Dahlgren7f0151d2014-01-02 16:08:12 -08002146 mParameters.setFlashMode(flashMode);
2147 }
2148 }
2149
Sascha Haeberling638e6f02013-09-18 14:28:51 -07002150 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Michael Kolb8872c232013-01-29 10:33:22 -08002151 private void updateAutoFocusMoveCallback() {
Angus Kongb50b5cb2013-08-09 14:55:20 -07002152 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
Angus Kong9ef99252013-07-18 18:04:19 -07002153 mCameraDevice.setAutoFocusMoveCallback(mHandler,
Angus Kong4f795b82013-09-16 14:25:35 -07002154 (CameraAFMoveCallback) mAutoFocusMoveCallback);
Michael Kolb8872c232013-01-29 10:33:22 -08002155 } else {
Angus Kong9ef99252013-07-18 18:04:19 -07002156 mCameraDevice.setAutoFocusMoveCallback(null, null);
Michael Kolb8872c232013-01-29 10:33:22 -08002157 }
2158 }
2159
Spike Spragueabf54e22014-03-27 15:41:28 -07002160 /**
2161 * Sets the exposure compensation to the given value and also updates settings.
2162 *
2163 * @param value exposure compensation value to be set
2164 */
2165 public void setExposureCompensation(int value) {
Angus Kong88289042014-04-22 16:39:42 -07002166 int max = mCameraCapabilities.getMaxExposureCompensation();
2167 int min = mCameraCapabilities.getMinExposureCompensation();
Spike Spragueabf54e22014-03-27 15:41:28 -07002168 if (value >= min && value <= max) {
2169 mParameters.setExposureCompensation(value);
2170 SettingsManager settingsManager = mActivity.getSettingsManager();
2171 settingsManager.set(SettingsManager.SETTING_EXPOSURE_COMPENSATION_VALUE, Integer.toString(value));
2172 } else {
2173 Log.w(TAG, "invalid exposure range: " + value);
2174 }
2175 }
2176
Michael Kolb8872c232013-01-29 10:33:22 -08002177 // We separate the parameters into several subsets, so we can update only
2178 // the subsets actually need updating. The PREFERENCE set needs extra
2179 // locking because the preference can be changed from GLThread as well.
2180 private void setCameraParameters(int updateSet) {
2181 if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {
2182 updateCameraParametersInitialize();
2183 }
2184
2185 if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {
2186 updateCameraParametersZoom();
2187 }
2188
2189 if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {
Erin Dahlgrenba6994d2013-12-12 16:04:38 -08002190 updateCameraParametersPreference();
Michael Kolb8872c232013-01-29 10:33:22 -08002191 }
2192
2193 mCameraDevice.setParameters(mParameters);
2194 }
2195
2196 // If the Camera is idle, update the parameters immediately, otherwise
2197 // accumulate them in mUpdateSet and update later.
2198 private void setCameraParametersWhenIdle(int additionalUpdateSet) {
2199 mUpdateSet |= additionalUpdateSet;
2200 if (mCameraDevice == null) {
2201 // We will update all the parameters when we open the device, so
2202 // we don't need to do anything now.
2203 mUpdateSet = 0;
2204 return;
2205 } else if (isCameraIdle()) {
2206 setCameraParameters(mUpdateSet);
Michael Kolbd6954f32013-03-08 20:43:01 -08002207 updateSceneMode();
Michael Kolb8872c232013-01-29 10:33:22 -08002208 mUpdateSet = 0;
2209 } else {
Angus Kong13e87c42013-11-25 10:02:47 -08002210 if (!mHandler.hasMessages(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE)) {
2211 mHandler.sendEmptyMessageDelayed(MSG_SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08002212 }
2213 }
2214 }
2215
ztenghui7b265a62013-09-09 14:58:44 -07002216 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002217 public boolean isCameraIdle() {
Michael Kolb8872c232013-01-29 10:33:22 -08002218 return (mCameraState == IDLE) ||
2219 (mCameraState == PREVIEW_STOPPED) ||
2220 ((mFocusManager != null) && mFocusManager.isFocusCompleted()
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002221 && (mCameraState != SWITCHING_CAMERA));
Michael Kolb8872c232013-01-29 10:33:22 -08002222 }
2223
ztenghui7b265a62013-09-09 14:58:44 -07002224 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002225 public boolean isImageCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -08002226 String action = mActivity.getIntent().getAction();
2227 return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01002228 || CameraActivity.ACTION_IMAGE_CAPTURE_SECURE.equals(action));
Michael Kolb8872c232013-01-29 10:33:22 -08002229 }
2230
2231 private void setupCaptureParams() {
2232 Bundle myExtras = mActivity.getIntent().getExtras();
2233 if (myExtras != null) {
2234 mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
2235 mCropValue = myExtras.getString("crop");
2236 }
2237 }
2238
Michael Kolb8872c232013-01-29 10:33:22 -08002239 private void initializeCapabilities() {
2240 mInitialParams = mCameraDevice.getParameters();
Angus Kong88289042014-04-22 16:39:42 -07002241 mCameraCapabilities = mCameraDevice.getCapabilities();
2242 mFocusAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA);
2243 mMeteringAreaSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
2244 mAeLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK);
2245 mAwbLockSupported = mCameraCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK);
Angus Kongdcccc512013-08-08 17:06:03 -07002246 mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains(
Angus Kongb50b5cb2013-08-09 14:55:20 -07002247 CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE);
Michael Kolb8872c232013-01-29 10:33:22 -08002248 }
2249
Marco Nelissen0744e4a2013-11-22 01:47:37 +00002250 // TODO: Remove this
Michael Kolb8872c232013-01-29 10:33:22 -08002251 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002252 public int onZoomChanged(int index) {
2253 // Not useful to change zoom value when the activity is paused.
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002254 if (mPaused) {
2255 return index;
2256 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002257 mZoomValue = index;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002258 if (mParameters == null || mCameraDevice == null) {
2259 return index;
2260 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002261 // Set zoom parameters asynchronously
2262 mParameters.setZoom(mZoomValue);
Angus Kong36ed8672013-04-15 12:11:15 -07002263 mCameraDevice.setParameters(mParameters);
Michael Kolbd6954f32013-03-08 20:43:01 -08002264 Parameters p = mCameraDevice.getParameters();
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002265 if (p != null) {
2266 return p.getZoom();
2267 }
Michael Kolbd6954f32013-03-08 20:43:01 -08002268 return index;
Angus Kongce5480e2013-01-29 17:43:48 -08002269 }
2270
2271 @Override
Michael Kolbd6954f32013-03-08 20:43:01 -08002272 public int getCameraState() {
2273 return mCameraState;
2274 }
2275
2276 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002277 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07002278 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Angus Kongce5480e2013-01-29 17:43:48 -08002279 }
Angus Kong86d36312013-01-31 18:22:44 -08002280
2281 @Override
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002282 public void onLowMemory() {
2283 // Not much we can do in the photo module.
Angus Kong86d36312013-01-31 18:22:44 -08002284 }
Angus Kong0d00a892013-03-26 11:40:40 -07002285
2286 @Override
2287 public void onAccuracyChanged(Sensor sensor, int accuracy) {
2288 }
2289
2290 @Override
2291 public void onSensorChanged(SensorEvent event) {
2292 int type = event.sensor.getType();
2293 float[] data;
2294 if (type == Sensor.TYPE_ACCELEROMETER) {
2295 data = mGData;
2296 } else if (type == Sensor.TYPE_MAGNETIC_FIELD) {
2297 data = mMData;
2298 } else {
2299 // we should not be here.
2300 return;
2301 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08002302 for (int i = 0; i < 3; i++) {
Angus Kong0d00a892013-03-26 11:40:40 -07002303 data[i] = event.values[i];
2304 }
2305 float[] orientation = new float[3];
2306 SensorManager.getRotationMatrix(mR, null, mGData, mMData);
2307 SensorManager.getOrientation(mR, orientation);
2308 mHeading = (int) (orientation[0] * 180f / Math.PI) % 360;
2309 if (mHeading < 0) {
2310 mHeading += 360;
2311 }
Angus Kong0d00a892013-03-26 11:40:40 -07002312 }
Doris Liu6432cd62013-06-13 17:20:31 -07002313
Ruben Brunkd217ed02013-10-08 23:31:13 -07002314 // For debugging only.
2315 public void setDebugUri(Uri uri) {
2316 mDebugUri = uri;
2317 }
2318
2319 // For debugging only.
2320 private void saveToDebugUri(byte[] data) {
2321 if (mDebugUri != null) {
2322 OutputStream outputStream = null;
2323 try {
2324 outputStream = mContentResolver.openOutputStream(mDebugUri);
2325 outputStream.write(data);
2326 outputStream.close();
2327 } catch (IOException e) {
2328 Log.e(TAG, "Exception while writing debug jpeg file", e);
2329 } finally {
2330 CameraUtil.closeSilently(outputStream);
2331 }
2332 }
2333 }
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002334
2335 @Override
2336 public void onRemoteShutterPress() {
Sascha Haeberling57d4f742014-06-25 11:05:49 -07002337 mHandler.post(new Runnable() {
2338 @Override
2339 public void run() {
2340 focusAndCapture();
2341 }
2342 });
Sascha Haeberlingf2994f02014-01-23 19:55:01 -08002343 }
Doris Liu6c751642014-05-05 18:43:26 -07002344
2345 /**
2346 * This class manages the loading/releasing/playing of the sounds needed for
2347 * countdown timer.
2348 */
2349 private class CountdownSoundPlayer {
2350 private SoundPool mSoundPool;
2351 private int mBeepOnce;
2352 private int mBeepTwice;
2353
2354 void loadSounds() {
2355 // Load the beeps.
Seth Raphael20e20c92014-05-27 09:45:26 -07002356 if (mSoundPool == null) {
2357 mSoundPool = new SoundPool(1, AudioManager.STREAM_NOTIFICATION, 0);
2358 mBeepOnce = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_once, 1);
2359 mBeepTwice = mSoundPool.load(mAppController.getAndroidContext(), R.raw.beep_twice, 1);
2360 }
Doris Liu6c751642014-05-05 18:43:26 -07002361 }
2362
2363 void onRemainingSecondsChanged(int newVal) {
2364 if (mSoundPool == null) {
2365 Log.e(TAG, "Cannot play sound - they have not been loaded.");
2366 return;
2367 }
2368 if (newVal == 1) {
2369 mSoundPool.play(mBeepTwice, 1.0f, 1.0f, 0, 0, 1.0f);
2370 } else if (newVal == 2 || newVal == 3) {
2371 mSoundPool.play(mBeepOnce, 1.0f, 1.0f, 0, 0, 1.0f);
2372 }
2373 }
2374
2375 void release() {
Seth Raphael20e20c92014-05-27 09:45:26 -07002376 if (mSoundPool != null) {
2377 mSoundPool.release();
2378 mSoundPool = null;
2379 }
Doris Liu6c751642014-05-05 18:43:26 -07002380 }
2381 }
Michael Kolb8872c232013-01-29 10:33:22 -08002382}