blob: 2424ccd78d7b8745a773a7db67f84aeb5ba22192 [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;
21import android.content.ActivityNotFoundException;
22import android.content.BroadcastReceiver;
23import android.content.ContentResolver;
24import android.content.ContentValues;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
Michael Kolb8872c232013-01-29 10:33:22 -080028import android.graphics.Bitmap;
Angus Kong5f8c30e2014-03-06 17:15:08 -080029import android.graphics.Point;
Michael Kolb8872c232013-01-29 10:33:22 -080030import android.graphics.SurfaceTexture;
31import android.hardware.Camera.CameraInfo;
32import android.hardware.Camera.Parameters;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.hardware.Camera.Size;
34import android.location.Location;
Marco Nelissen20694b22013-10-29 15:27:24 -070035import android.media.AudioManager;
Michael Kolb8872c232013-01-29 10:33:22 -080036import android.media.CamcorderProfile;
37import android.media.CameraProfile;
38import android.media.MediaRecorder;
39import android.net.Uri;
40import android.os.Build;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.Message;
44import android.os.ParcelFileDescriptor;
45import android.os.SystemClock;
46import android.provider.MediaStore;
Ruben Brunk16007962013-04-19 15:27:57 -070047import android.provider.MediaStore.MediaColumns;
Michael Kolb8872c232013-01-29 10:33:22 -080048import android.provider.MediaStore.Video;
Michael Kolb8872c232013-01-29 10:33:22 -080049import android.view.KeyEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080050import android.view.OrientationEventListener;
Michael Kolb8872c232013-01-29 10:33:22 -080051import android.view.View;
Michael Kolb8872c232013-01-29 10:33:22 -080052import android.widget.Toast;
53
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080054import com.android.camera.app.AppController;
Spike Sprague39f8a762014-01-13 14:22:18 -080055import com.android.camera.app.CameraAppUI;
Doris Liua1ec04a2014-01-13 17:29:40 -080056import com.android.camera.app.CameraManager;
Angus Kong20fad242013-11-11 18:23:46 -080057import com.android.camera.app.CameraManager.CameraPictureCallback;
58import com.android.camera.app.CameraManager.CameraProxy;
Kevin Gabayanffbc43c2013-12-09 11:41:50 -080059import com.android.camera.app.LocationManager;
Angus Kongfd4fc0e2013-11-07 15:38:09 -080060import com.android.camera.app.MediaSaver;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -080061import com.android.camera.app.MemoryManager;
62import com.android.camera.app.MemoryManager.MemoryListener;
Angus Kong5596b4c2014-03-11 16:27:30 -070063import com.android.camera.debug.Log;
ztenghuia16e7b52013-08-23 11:47:56 -070064import com.android.camera.exif.ExifInterface;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -080065import com.android.camera.hardware.HardwareSpec;
66import com.android.camera.hardware.HardwareSpecImpl;
Angus Kong20fad242013-11-11 18:23:46 -080067import com.android.camera.module.ModuleController;
Erin Dahlgrenbd3da262013-12-02 11:48:13 -080068import com.android.camera.settings.SettingsManager;
Sascha Haeberlingde303232014-02-07 02:30:53 +010069import com.android.camera.settings.SettingsUtil;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070070import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070071import com.android.camera.util.CameraUtil;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070072import com.android.camera.util.UsageStatistics;
73import com.android.camera2.R;
Seth Raphael5e09d012013-12-18 13:45:03 -080074import com.google.common.logging.eventprotos;
Michael Kolb8872c232013-01-29 10:33:22 -080075
Angus Kongb50b5cb2013-08-09 14:55:20 -070076import java.io.File;
77import java.io.IOException;
78import java.text.SimpleDateFormat;
79import java.util.Date;
80import java.util.Iterator;
81import java.util.List;
82
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080083public class VideoModule extends CameraModule
84 implements ModuleController,
85 VideoController,
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -080086 MemoryListener,
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080087 MediaRecorder.OnErrorListener,
Doris Liua1ec04a2014-01-13 17:29:40 -080088 MediaRecorder.OnInfoListener, FocusOverlayManager.Listener {
Michael Kolb8872c232013-01-29 10:33:22 -080089
Angus Kong5596b4c2014-03-11 16:27:30 -070090 private static final Log.Tag TAG = new Log.Tag("VideoModule");
Michael Kolb8872c232013-01-29 10:33:22 -080091
Angus Kong13e87c42013-11-25 10:02:47 -080092 // Messages defined for the UI thread handler.
93 private static final int MSG_CHECK_DISPLAY_ROTATION = 4;
94 private static final int MSG_UPDATE_RECORD_TIME = 5;
95 private static final int MSG_ENABLE_SHUTTER_BUTTON = 6;
Angus Kong13e87c42013-11-25 10:02:47 -080096 private static final int MSG_SWITCH_CAMERA = 8;
97 private static final int MSG_SWITCH_CAMERA_START_ANIMATION = 9;
Michael Kolb8872c232013-01-29 10:33:22 -080098
99 private static final long SHUTTER_BUTTON_TIMEOUT = 500L; // 500ms
100
101 /**
102 * An unpublished intent flag requesting to start recording straight away
103 * and return as soon as recording is stopped.
104 * TODO: consider publishing by moving into MediaStore.
105 */
106 private static final String EXTRA_QUICK_CAPTURE =
107 "android.intent.extra.quickCapture";
108
Michael Kolb8872c232013-01-29 10:33:22 -0800109 // module fields
110 private CameraActivity mActivity;
Michael Kolb8872c232013-01-29 10:33:22 -0800111 private boolean mPaused;
Spike Sprague51c877c2014-02-18 11:14:12 -0800112
113 // if, during and intent capture, the activity is paused (e.g. when app switching or reviewing a
114 // shot video), we don't want the bottom bar intent ui to reset to the capture button
115 private boolean mDontResetIntentUiOnResume;
116
Michael Kolb8872c232013-01-29 10:33:22 -0800117 private int mCameraId;
118 private Parameters mParameters;
119
Doris Liu6432cd62013-06-13 17:20:31 -0700120 private boolean mIsInReviewMode;
Michael Kolb8872c232013-01-29 10:33:22 -0800121 private boolean mSnapshotInProgress = false;
122
Michael Kolb8872c232013-01-29 10:33:22 -0800123 private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
124
Angus Kong395ee2d2013-07-15 12:42:41 -0700125 // Preference must be read before starting preview. We check this before starting
126 // preview.
127 private boolean mPreferenceRead;
Michael Kolb8872c232013-01-29 10:33:22 -0800128
Michael Kolb8872c232013-01-29 10:33:22 -0800129 private boolean mIsVideoCaptureIntent;
130 private boolean mQuickCapture;
131
132 private MediaRecorder mMediaRecorder;
Michael Kolb8872c232013-01-29 10:33:22 -0800133
134 private boolean mSwitchingCamera;
135 private boolean mMediaRecorderRecording = false;
136 private long mRecordingStartTime;
137 private boolean mRecordingTimeCountsDown = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800138 private long mOnResumeTime;
139 // The video file that the hardware camera is about to record into
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800140 // (or is recording into.
Michael Kolb8872c232013-01-29 10:33:22 -0800141 private String mVideoFilename;
142 private ParcelFileDescriptor mVideoFileDescriptor;
143
144 // The video file that has already been recorded, and that is being
145 // examined by the user.
146 private String mCurrentVideoFilename;
147 private Uri mCurrentVideoUri;
ztenghui70bd0242013-10-14 11:15:44 -0700148 private boolean mCurrentVideoUriFromMediaSaved;
Michael Kolb8872c232013-01-29 10:33:22 -0800149 private ContentValues mCurrentVideoValues;
150
151 private CamcorderProfile mProfile;
152
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800153 // The video duration limit. 0 means no limit.
Michael Kolb8872c232013-01-29 10:33:22 -0800154 private int mMaxVideoDurationInMs;
155
156 // Time Lapse parameters.
Sascha Haeberling8c000c22014-03-17 13:56:05 -0700157 private final boolean mCaptureTimeLapse = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800158 // Default 0. If it is larger than 0, the camcorder is in time lapse mode.
Sascha Haeberling8c000c22014-03-17 13:56:05 -0700159 private final int mTimeBetweenTimeLapseFrameCaptureMs = 0;
Michael Kolb8872c232013-01-29 10:33:22 -0800160
161 boolean mPreviewing = false; // True if preview is started.
162 // The display rotation in degrees. This is only valid when mPreviewing is
163 // true.
164 private int mDisplayRotation;
165 private int mCameraDisplayOrientation;
Doris Liu2b906b82013-12-10 16:34:08 -0800166 private AppController mAppController;
Michael Kolb8872c232013-01-29 10:33:22 -0800167
Doris Liu6827ce22013-03-12 19:24:28 -0700168 private int mDesiredPreviewWidth;
169 private int mDesiredPreviewHeight;
Michael Kolb8872c232013-01-29 10:33:22 -0800170 private ContentResolver mContentResolver;
171
172 private LocationManager mLocationManager;
173
Michael Kolb8872c232013-01-29 10:33:22 -0800174 private int mPendingSwitchCameraId;
Michael Kolb8872c232013-01-29 10:33:22 -0800175 private final Handler mHandler = new MainHandler();
Doris Liu6827ce22013-03-12 19:24:28 -0700176 private VideoUI mUI;
Doris Liu6432cd62013-06-13 17:20:31 -0700177 private CameraProxy mCameraDevice;
178
Michael Kolb8872c232013-01-29 10:33:22 -0800179 // The degrees of the device rotated clockwise from its natural orientation.
180 private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
181
182 private int mZoomValue; // The current zoom value.
Doris Liu6827ce22013-03-12 19:24:28 -0700183
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800184 private final MediaSaver.OnMediaSavedListener mOnVideoSavedListener =
185 new MediaSaver.OnMediaSavedListener() {
Angus Kong83a99ae2013-04-17 15:37:07 -0700186 @Override
187 public void onMediaSaved(Uri uri) {
188 if (uri != null) {
Doris Liu2a7f44c2013-08-12 15:18:53 -0700189 mCurrentVideoUri = uri;
ztenghui70bd0242013-10-14 11:15:44 -0700190 mCurrentVideoUriFromMediaSaved = true;
Doris Liu2a7f44c2013-08-12 15:18:53 -0700191 onVideoSaved();
Sascha Haeberling37f36112013-08-06 14:31:52 -0700192 mActivity.notifyNewMedia(uri);
Angus Kong83a99ae2013-04-17 15:37:07 -0700193 }
194 }
195 };
196
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800197 private final MediaSaver.OnMediaSavedListener mOnPhotoSavedListener =
198 new MediaSaver.OnMediaSavedListener() {
Angus Kong86d36312013-01-31 18:22:44 -0800199 @Override
200 public void onMediaSaved(Uri uri) {
201 if (uri != null) {
Sascha Haeberling37f36112013-08-06 14:31:52 -0700202 mActivity.notifyNewMedia(uri);
Angus Kong86d36312013-01-31 18:22:44 -0800203 }
204 }
205 };
Doris Liua1ec04a2014-01-13 17:29:40 -0800206 private FocusOverlayManager mFocusManager;
207 private boolean mMirror;
208 private Parameters mInitialParams;
209 private boolean mFocusAreaSupported;
210 private boolean mMeteringAreaSupported;
211
212 private final CameraManager.CameraAFCallback mAutoFocusCallback =
213 new CameraManager.CameraAFCallback() {
214 @Override
215 public void onAutoFocus(boolean focused, CameraProxy camera) {
216 if (mPaused) {
217 return;
218 }
219 mFocusManager.onAutoFocus(focused, false);
220 }
221 };
222
223 private final Object mAutoFocusMoveCallback =
224 ApiHelper.HAS_AUTO_FOCUS_MOVE_CALLBACK
225 ? new CameraManager.CameraAFMoveCallback() {
226 @Override
227 public void onAutoFocusMoving(boolean moving, CameraProxy camera) {
228 mFocusManager.onAutoFocusMoving(moving);
229 }
230 } : null;
Angus Kong86d36312013-01-31 18:22:44 -0800231
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -0800232 /**
233 * This Handler is used to post message back onto the main thread of the
234 * application.
235 */
Michael Kolb8872c232013-01-29 10:33:22 -0800236 private class MainHandler extends Handler {
237 @Override
238 public void handleMessage(Message msg) {
239 switch (msg.what) {
240
Angus Kong13e87c42013-11-25 10:02:47 -0800241 case MSG_ENABLE_SHUTTER_BUTTON:
Erin Dahlgren667630d2014-04-01 14:03:25 -0700242 mAppController.setShutterEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800243 break;
244
Angus Kong13e87c42013-11-25 10:02:47 -0800245 case MSG_UPDATE_RECORD_TIME: {
Michael Kolb8872c232013-01-29 10:33:22 -0800246 updateRecordingTime();
247 break;
248 }
249
Angus Kong13e87c42013-11-25 10:02:47 -0800250 case MSG_CHECK_DISPLAY_ROTATION: {
Michael Kolb8872c232013-01-29 10:33:22 -0800251 // Restart the preview if display rotation has changed.
252 // Sometimes this happens when the device is held upside
253 // down and camera app is opened. Rotation animation will
254 // take some time and the rotation value we have got may be
255 // wrong. Framework does not have a callback for this now.
Angus Kongb50b5cb2013-08-09 14:55:20 -0700256 if ((CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation)
Michael Kolb8872c232013-01-29 10:33:22 -0800257 && !mMediaRecorderRecording && !mSwitchingCamera) {
258 startPreview();
259 }
260 if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
Angus Kong13e87c42013-11-25 10:02:47 -0800261 mHandler.sendEmptyMessageDelayed(MSG_CHECK_DISPLAY_ROTATION, 100);
Michael Kolb8872c232013-01-29 10:33:22 -0800262 }
263 break;
264 }
265
Angus Kong13e87c42013-11-25 10:02:47 -0800266 case MSG_SWITCH_CAMERA: {
Michael Kolb8872c232013-01-29 10:33:22 -0800267 switchCamera();
268 break;
269 }
270
Angus Kong13e87c42013-11-25 10:02:47 -0800271 case MSG_SWITCH_CAMERA_START_ANIMATION: {
Doris Liu6432cd62013-06-13 17:20:31 -0700272 //TODO:
273 //((CameraScreenNail) mActivity.mCameraScreenNail).animateSwitchCamera();
Michael Kolb8872c232013-01-29 10:33:22 -0800274
275 // Enable all camera controls.
276 mSwitchingCamera = false;
277 break;
278 }
279
Michael Kolb8872c232013-01-29 10:33:22 -0800280 default:
281 Log.v(TAG, "Unhandled message: " + msg.what);
282 break;
283 }
284 }
285 }
286
287 private BroadcastReceiver mReceiver = null;
288
289 private class MyBroadcastReceiver extends BroadcastReceiver {
290 @Override
291 public void onReceive(Context context, Intent intent) {
292 String action = intent.getAction();
293 if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
294 stopVideoRecording();
295 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) {
296 Toast.makeText(mActivity,
297 mActivity.getResources().getString(R.string.wait), Toast.LENGTH_LONG).show();
298 }
299 }
300 }
301
Spike Sprague41f68002014-01-24 16:59:25 -0800302 private int mShutterIconId;
303
304
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800305 /**
306 * Construct a new video module.
307 */
Angus Kongc4e66562013-11-22 23:03:21 -0800308 public VideoModule(AppController app) {
309 super(app);
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800310 }
311
Michael Kolb8872c232013-01-29 10:33:22 -0800312 private String createName(long dateTaken) {
313 Date date = new Date(dateTaken);
314 SimpleDateFormat dateFormat = new SimpleDateFormat(
315 mActivity.getString(R.string.video_file_name_format));
316
317 return dateFormat.format(date);
318 }
319
Michael Kolb8872c232013-01-29 10:33:22 -0800320 @Override
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100321 public void init(CameraActivity activity, boolean isSecureCamera, boolean isCaptureIntent) {
322 mActivity = activity;
323 // TODO: Need to look at the controller interface to see if we can get
324 // rid of passing in the activity directly.
325 mAppController = mActivity;
326 mUI = new VideoUI(mActivity, this, mActivity.getModuleLayoutRoot());
327 mActivity.setPreviewStatusListener(mUI);
Michael Kolb8872c232013-01-29 10:33:22 -0800328
Erin Dahlgrenbd3da262013-12-02 11:48:13 -0800329 SettingsManager settingsManager = mActivity.getSettingsManager();
330 mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
Michael Kolb8872c232013-01-29 10:33:22 -0800331
Michael Kolb8872c232013-01-29 10:33:22 -0800332 /*
333 * To reduce startup time, we start the preview in another thread.
334 * We make sure the preview is started at the end of onCreate.
335 */
Angus Kong20fad242013-11-11 18:23:46 -0800336 requestCamera(mCameraId);
Michael Kolb8872c232013-01-29 10:33:22 -0800337
338 mContentResolver = mActivity.getContentResolver();
339
Michael Kolb8872c232013-01-29 10:33:22 -0800340 // Surface texture is from camera screen nail and startPreview needs it.
341 // This must be done before startPreview.
342 mIsVideoCaptureIntent = isVideoCaptureIntent();
Michael Kolb8872c232013-01-29 10:33:22 -0800343
Michael Kolb8872c232013-01-29 10:33:22 -0800344 mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
Erin Dahlgren21c21a62013-11-19 16:37:38 -0800345 mLocationManager = mActivity.getLocationManager();
Michael Kolb8872c232013-01-29 10:33:22 -0800346
Doris Liu6827ce22013-03-12 19:24:28 -0700347 mUI.setOrientationIndicator(0, false);
Michael Kolb8872c232013-01-29 10:33:22 -0800348 setDisplayOrientation();
349
Doris Liu6827ce22013-03-12 19:24:28 -0700350 mUI.showTimeLapseUI(mCaptureTimeLapse);
Michael Kolb8872c232013-01-29 10:33:22 -0800351 mPendingSwitchCameraId = -1;
Spike Sprague41f68002014-01-24 16:59:25 -0800352
353 mShutterIconId = CameraUtil.getCameraShutterIconId(
354 mAppController.getCurrentModuleIndex(), mAppController.getAndroidContext());
355
Doris Liu6827ce22013-03-12 19:24:28 -0700356 }
357
Erin Dahlgren4efa8b52013-12-17 18:31:35 -0800358 @Override
359 public boolean isUsingBottomBar() {
360 return true;
361 }
362
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800363 private void initializeControlByIntent() {
364 if (isVideoCaptureIntent()) {
Spike Sprague51c877c2014-02-18 11:14:12 -0800365 if (!mDontResetIntentUiOnResume) {
366 mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
367 }
368 // reset the flag
369 mDontResetIntentUiOnResume = false;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800370 }
371 }
372
Doris Liu6827ce22013-03-12 19:24:28 -0700373 @Override
374 public void onSingleTapUp(View view, int x, int y) {
Doris Liu057b21a2014-04-25 17:35:27 -0700375 if (mPaused || mCameraDevice == null) {
376 return;
377 }
378 if (mMediaRecorderRecording) {
379 if (!mSnapshotInProgress) {
380 takeASnapshot();
381 }
Doris Liua1ec04a2014-01-13 17:29:40 -0800382 return;
383 }
384 // Check if metering area or focus area is supported.
385 if (!mFocusAreaSupported && !mMeteringAreaSupported) {
386 return;
387 }
388 // Tap to focus.
389 mFocusManager.onSingleTapUp(x, y);
Doris Liu38605742013-08-13 15:01:52 -0700390 }
391
392 private void takeASnapshot() {
Sascha Haeberlinga514b142013-08-13 16:10:01 -0700393 // Only take snapshots if video snapshot is supported by device
Doris Liu46836732014-04-29 18:15:55 -0700394 if(!mParameters.isVideoSnapshotSupported()) {
395 Log.w(TAG, "Cannot take a video snapshot - not supported by hardware");
396 return;
397 }
398 if (!mIsVideoCaptureIntent) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700399 if (!mMediaRecorderRecording || mPaused || mSnapshotInProgress
Doris Liu057b21a2014-04-25 17:35:27 -0700400 || !mAppController.isShutterEnabled()) {
Doris Liu38605742013-08-13 15:01:52 -0700401 return;
402 }
403
404 // Set rotation and gps data.
Sascha Haeberlinga7cbfc02014-02-14 11:06:03 +0100405 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
406 int rotation = CameraUtil.getJpegRotation(info, mOrientation);
Doris Liu38605742013-08-13 15:01:52 -0700407 mParameters.setRotation(rotation);
408 Location loc = mLocationManager.getCurrentLocation();
409 CameraUtil.setGpsParameters(mParameters, loc);
410 mCameraDevice.setParameters(mParameters);
411
Doris Liu057b21a2014-04-25 17:35:27 -0700412 Log.i(TAG, "Video snapshot start");
Doris Liu38605742013-08-13 15:01:52 -0700413 mCameraDevice.takePicture(mHandler,
414 null, null, null, new JpegPictureCallback(loc));
415 showVideoSnapshotUI(true);
416 mSnapshotInProgress = true;
Doris Liu6827ce22013-03-12 19:24:28 -0700417 }
Michael Kolb8872c232013-01-29 10:33:22 -0800418 }
419
Doris Liua1ec04a2014-01-13 17:29:40 -0800420 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
421 private void updateAutoFocusMoveCallback() {
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800422 if (mPaused) {
423 return;
424 }
425
Doris Liua1ec04a2014-01-13 17:29:40 -0800426 if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
427 mCameraDevice.setAutoFocusMoveCallback(mHandler,
428 (CameraManager.CameraAFMoveCallback) mAutoFocusMoveCallback);
429 } else {
430 mCameraDevice.setAutoFocusMoveCallback(null, null);
431 }
432 }
433
434 /**
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700435 * @return Whether the currently active camera is front-facing.
436 */
437 private boolean isCameraFrontFacing() {
438 CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
439 return info.facing == CameraInfo.CAMERA_FACING_FRONT;
440 }
441
442 /**
Doris Liua1ec04a2014-01-13 17:29:40 -0800443 * The focus manager gets initialized after camera is available.
444 */
445 private void initializeFocusManager() {
446 // Create FocusManager object. startPreview needs it.
447 // if mFocusManager not null, reuse it
448 // otherwise create a new instance
449 if (mFocusManager != null) {
450 mFocusManager.removeMessages();
451 } else {
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700452 mMirror = isCameraFrontFacing();
Doris Liua1ec04a2014-01-13 17:29:40 -0800453 String[] defaultFocusModes = mActivity.getResources().getStringArray(
454 R.array.pref_camera_focusmode_default_array);
455 mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
456 defaultFocusModes,
457 mInitialParams, this, mMirror,
458 mActivity.getMainLooper(), mUI.getFocusUI());
459 }
460 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
461 }
462
Michael Kolb8872c232013-01-29 10:33:22 -0800463 @Override
464 public void onOrientationChanged(int orientation) {
465 // We keep the last known orientation. So if the user first orient
466 // the camera then point the camera to floor or sky, we still have
467 // the correct orientation.
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100468 if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
469 return;
470 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700471 int newOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800472
473 if (mOrientation != newOrientation) {
474 mOrientation = newOrientation;
Michael Kolb8872c232013-01-29 10:33:22 -0800475 }
476
Michael Kolb8872c232013-01-29 10:33:22 -0800477 }
478
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800479 private final ButtonManager.ButtonCallback mFlashCallback =
Erin Dahlgrenfce8a0b2013-12-12 17:36:32 -0800480 new ButtonManager.ButtonCallback() {
481 @Override
482 public void onStateChanged(int state) {
483 // Update flash parameters.
484 enableTorchMode(true);
485 }
486 };
487
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800488 private final ButtonManager.ButtonCallback mCameraCallback =
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800489 new ButtonManager.ButtonCallback() {
490 @Override
491 public void onStateChanged(int state) {
Angus Kong97e282a2014-03-04 18:44:49 -0800492 if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
Erin Dahlgren18e2ef62013-12-05 14:53:38 -0800493 return;
494 }
495 mPendingSwitchCameraId = state;
496 Log.d(TAG, "Start to copy texture.");
497
498 // Disable all camera controls.
499 mSwitchingCamera = true;
500 switchCamera();
501 }
502 };
503
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800504 private final View.OnClickListener mCancelCallback = new View.OnClickListener() {
505 @Override
506 public void onClick(View v) {
507 onReviewCancelClicked(v);
508 }
509 };
510
511 private final View.OnClickListener mDoneCallback = new View.OnClickListener() {
512 @Override
513 public void onClick(View v) {
514 onReviewDoneClicked(v);
515 }
516 };
517 private final View.OnClickListener mReviewCallback = new View.OnClickListener() {
518 @Override
519 public void onClick(View v) {
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800520 onReviewPlayClicked(v);
521 }
522 };
523
Angus Kong20fad242013-11-11 18:23:46 -0800524 @Override
Erin Dahlgren1ca516f2014-03-28 12:44:04 -0700525 public void hardResetSettings(SettingsManager settingsManager) {
526 // VideoModule does not need to hard reset any settings.
527 }
528
529 @Override
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800530 public HardwareSpec getHardwareSpec() {
Erin Dahlgren49ab9222014-01-28 17:40:28 -0800531 return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800532 }
533
534 @Override
535 public CameraAppUI.BottomBarUISpec getBottomBarSpec() {
536 CameraAppUI.BottomBarUISpec bottomBarSpec = new CameraAppUI.BottomBarUISpec();
537
538 bottomBarSpec.enableCamera = true;
539 bottomBarSpec.cameraCallback = mCameraCallback;
540 bottomBarSpec.enableTorchFlash = true;
541 bottomBarSpec.flashCallback = mFlashCallback;
542 bottomBarSpec.hideHdr = true;
Spike Spragueab3adde2014-03-10 12:19:08 -0700543 bottomBarSpec.enableGridLines = true;
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800544
545 if (isVideoCaptureIntent()) {
546 bottomBarSpec.showCancel = true;
547 bottomBarSpec.cancelCallback = mCancelCallback;
548 bottomBarSpec.showDone = true;
549 bottomBarSpec.doneCallback = mDoneCallback;
550 bottomBarSpec.showReview = true;
551 bottomBarSpec.reviewCallback = mReviewCallback;
552 }
553
554 return bottomBarSpec;
Erin Dahlgren0a6a8d82014-01-09 22:17:38 -0800555 }
556
557 @Override
Angus Kong20fad242013-11-11 18:23:46 -0800558 public void onCameraAvailable(CameraProxy cameraProxy) {
559 mCameraDevice = cameraProxy;
Doris Liua1ec04a2014-01-13 17:29:40 -0800560 mInitialParams = mCameraDevice.getParameters();
561 mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams);
562 mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams);
Angus Kong20fad242013-11-11 18:23:46 -0800563 readVideoPreferences();
564 resizeForPreviewAspectRatio();
Doris Liua1ec04a2014-01-13 17:29:40 -0800565 initializeFocusManager();
Doris Liu5dd85a12014-04-15 11:45:55 -0700566 // TODO: Having focus overlay manager caching the parameters is prone to error,
567 // we should consider passing the parameters to focus overlay to ensure the
568 // parameters are up to date.
569 mFocusManager.setParameters(mInitialParams);
Doris Liu5a367542014-01-17 17:21:42 -0800570
Angus Kong20fad242013-11-11 18:23:46 -0800571 startPreview();
572 initializeVideoSnapshot();
Angus Kong20fad242013-11-11 18:23:46 -0800573 mUI.initializeZoom(mParameters);
Erin Dahlgrenb1641f52014-01-14 15:58:52 -0800574 initializeControlByIntent();
Angus Kong20fad242013-11-11 18:23:46 -0800575 }
576
Michael Kolb8872c232013-01-29 10:33:22 -0800577 private void startPlayVideoActivity() {
578 Intent intent = new Intent(Intent.ACTION_VIEW);
579 intent.setDataAndType(mCurrentVideoUri, convertOutputFormatToMimeType(mProfile.fileFormat));
580 try {
Sascha Haeberlingb7639c62013-09-09 14:42:43 -0700581 mActivity
582 .startActivityForResult(intent, CameraActivity.REQ_CODE_DONT_SWITCH_TO_PREVIEW);
Michael Kolb8872c232013-01-29 10:33:22 -0800583 } catch (ActivityNotFoundException ex) {
584 Log.e(TAG, "Couldn't view video " + mCurrentVideoUri, ex);
585 }
586 }
587
ztenghui7b265a62013-09-09 14:58:44 -0700588 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800589 @OnClickAttr
590 public void onReviewPlayClicked(View v) {
591 startPlayVideoActivity();
592 }
593
ztenghui7b265a62013-09-09 14:58:44 -0700594 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800595 @OnClickAttr
596 public void onReviewDoneClicked(View v) {
Doris Liu69ef5ea2013-05-07 13:48:10 -0700597 mIsInReviewMode = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800598 doReturnToCaller(true);
599 }
600
ztenghui7b265a62013-09-09 14:58:44 -0700601 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800602 @OnClickAttr
603 public void onReviewCancelClicked(View v) {
ztenghuiaf3a1972013-09-17 16:51:15 -0700604 // TODO: It should be better to not even insert the URI at all before we
605 // confirm done in review, which means we need to handle temporary video
606 // files in a quite different way than we currently had.
ztenghui70bd0242013-10-14 11:15:44 -0700607 // Make sure we don't delete the Uri sent from the video capture intent.
608 if (mCurrentVideoUriFromMediaSaved) {
ztenghuidfe5b152013-10-08 17:07:15 -0700609 mContentResolver.delete(mCurrentVideoUri, null, null);
610 }
ztenghui638bf9a2013-10-09 10:52:33 -0700611 mIsInReviewMode = false;
Michael Kolb8872c232013-01-29 10:33:22 -0800612 doReturnToCaller(false);
613 }
614
Doris Liu69ef5ea2013-05-07 13:48:10 -0700615 @Override
616 public boolean isInReviewMode() {
617 return mIsInReviewMode;
618 }
619
Michael Kolb8872c232013-01-29 10:33:22 -0800620 private void onStopVideoRecording() {
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800621 mAppController.getCameraAppUI().setSwipeEnabled(true);
Michael Kolb8872c232013-01-29 10:33:22 -0800622 boolean recordFail = stopVideoRecording();
623 if (mIsVideoCaptureIntent) {
Doris Liu3973deb2013-08-21 13:42:22 -0700624 if (mQuickCapture) {
625 doReturnToCaller(!recordFail);
626 } else if (!recordFail) {
627 showCaptureResult();
Michael Kolb8872c232013-01-29 10:33:22 -0800628 }
629 } else if (!recordFail){
630 // Start capture animation.
631 if (!mPaused && ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) {
632 // The capture animation is disabled on ICS because we use SurfaceView
633 // for preview during recording. When the recording is done, we switch
634 // back to use SurfaceTexture for preview and we need to stop then start
635 // the preview. This will cause the preview flicker since the preview
636 // will not be continuous for a short period of time.
Sascha Haeberling4f91ab52013-05-21 11:26:13 -0700637
Sascha Haeberling37f36112013-08-06 14:31:52 -0700638 mUI.animateFlash();
Michael Kolb8872c232013-01-29 10:33:22 -0800639 }
640 }
641 }
642
Doris Liu2a7f44c2013-08-12 15:18:53 -0700643 public void onVideoSaved() {
644 if (mIsVideoCaptureIntent) {
645 showCaptureResult();
646 }
647 }
648
Michael Kolb8872c232013-01-29 10:33:22 -0800649 public void onProtectiveCurtainClick(View v) {
650 // Consume clicks
651 }
652
653 @Override
654 public void onShutterButtonClick() {
Sascha Haeberling8793eff2014-01-15 16:33:59 -0800655 if (mSwitchingCamera) {
656 return;
657 }
Michael Kolb8872c232013-01-29 10:33:22 -0800658 boolean stop = mMediaRecorderRecording;
659
660 if (stop) {
661 onStopVideoRecording();
662 } else {
663 startVideoRecording();
664 }
Erin Dahlgren667630d2014-04-01 14:03:25 -0700665 mAppController.setShutterEnabled(false);
Doris Liua1ec04a2014-01-13 17:29:40 -0800666 mFocusManager.onShutterUp();
Michael Kolb8872c232013-01-29 10:33:22 -0800667
668 // Keep the shutter button disabled when in video capture intent
669 // mode and recording is stopped. It'll be re-enabled when
670 // re-take button is clicked.
671 if (!(mIsVideoCaptureIntent && stop)) {
Angus Kong13e87c42013-11-25 10:02:47 -0800672 mHandler.sendEmptyMessageDelayed(MSG_ENABLE_SHUTTER_BUTTON, SHUTTER_BUTTON_TIMEOUT);
Michael Kolb8872c232013-01-29 10:33:22 -0800673 }
674 }
675
676 @Override
677 public void onShutterButtonFocus(boolean pressed) {
Doris Liuf55f3c42013-11-20 00:24:46 -0800678 // TODO: Remove this when old camera controls are removed from the UI.
Michael Kolb8872c232013-01-29 10:33:22 -0800679 }
680
681 private void readVideoPreferences() {
682 // The preference stores values from ListPreference and is thus string type for all values.
683 // We need to convert it to int manually.
Erin Dahlgrenbd3da262013-12-02 11:48:13 -0800684 SettingsManager settingsManager = mActivity.getSettingsManager();
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700685 if (!settingsManager.isSet(SettingsManager.SETTING_VIDEO_QUALITY_BACK)) {
686 settingsManager.setDefault(SettingsManager.SETTING_VIDEO_QUALITY_BACK);
Doris Liu3f7e0042013-07-31 11:25:09 -0700687 }
Sascha Haeberling6ccec202014-03-11 09:44:34 -0700688 if (!settingsManager.isSet(SettingsManager.SETTING_VIDEO_QUALITY_FRONT)) {
689 settingsManager.setDefault(SettingsManager.SETTING_VIDEO_QUALITY_FRONT);
690 }
691 String videoQuality = settingsManager
692 .get(isCameraFrontFacing() ? SettingsManager.SETTING_VIDEO_QUALITY_FRONT
693 : SettingsManager.SETTING_VIDEO_QUALITY_BACK);
Sascha Haeberlingde303232014-02-07 02:30:53 +0100694 int quality = SettingsUtil.getVideoQuality(videoQuality, mCameraId);
695 Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
Michael Kolb8872c232013-01-29 10:33:22 -0800696
697 // Set video quality.
698 Intent intent = mActivity.getIntent();
699 if (intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) {
700 int extraVideoQuality =
701 intent.getIntExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
702 if (extraVideoQuality > 0) {
703 quality = CamcorderProfile.QUALITY_HIGH;
704 } else { // 0 is mms.
705 quality = CamcorderProfile.QUALITY_LOW;
706 }
707 }
708
709 // Set video duration limit. The limit is read from the preference,
710 // unless it is specified in the intent.
711 if (intent.hasExtra(MediaStore.EXTRA_DURATION_LIMIT)) {
712 int seconds =
713 intent.getIntExtra(MediaStore.EXTRA_DURATION_LIMIT, 0);
714 mMaxVideoDurationInMs = 1000 * seconds;
715 } else {
Sascha Haeberling4044ab72014-04-02 16:25:03 -0700716 mMaxVideoDurationInMs = SettingsManager.getMaxVideoDuration(mActivity
717 .getAndroidContext());
Michael Kolb8872c232013-01-29 10:33:22 -0800718 }
719
Sascha Haeberling8c000c22014-03-17 13:56:05 -0700720 // TODO: Uncomment this block to re-enable time-lapse.
721 /* // Read time lapse recording interval.
Erin Dahlgrenbd3da262013-12-02 11:48:13 -0800722 String frameIntervalStr = settingsManager.get(
723 SettingsManager.SETTING_VIDEO_TIME_LAPSE_FRAME_INTERVAL);
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700724 mTimeBetweenTimeLapseFrameCaptureMs = Integer.parseInt(frameIntervalStr);
725 mCaptureTimeLapse = (mTimeBetweenTimeLapseFrameCaptureMs != 0);
Michael Kolb8872c232013-01-29 10:33:22 -0800726 // TODO: This should be checked instead directly +1000.
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100727 if (mCaptureTimeLapse) {
728 quality += 1000;
Sascha Haeberling8c000c22014-03-17 13:56:05 -0700729 } */
Andy Huibers329e1012014-02-04 08:03:35 -0800730
731 // If quality is not supported, request QUALITY_HIGH which is always supported.
732 if (CamcorderProfile.hasProfile(mCameraId, quality) == false) {
733 quality = CamcorderProfile.QUALITY_HIGH;
734 }
Michael Kolb8872c232013-01-29 10:33:22 -0800735 mProfile = CamcorderProfile.get(mCameraId, quality);
Angus Kong395ee2d2013-07-15 12:42:41 -0700736 mPreferenceRead = true;
ztenghuief01a312013-10-14 15:25:16 -0700737 if (mCameraDevice == null) {
738 return;
739 }
Doris Liu6432cd62013-06-13 17:20:31 -0700740 mParameters = mCameraDevice.getParameters();
Angus Kong5f8c30e2014-03-06 17:15:08 -0800741 Point desiredPreviewSize = getDesiredPreviewSize(mAppController.getAndroidContext(),
742 mParameters, mProfile, mUI.getPreviewScreenSize());
743 mDesiredPreviewWidth = desiredPreviewSize.x;
744 mDesiredPreviewHeight = desiredPreviewSize.y;
Doris Liu6432cd62013-06-13 17:20:31 -0700745 mUI.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
Michael Kolb8872c232013-01-29 10:33:22 -0800746 Log.v(TAG, "mDesiredPreviewWidth=" + mDesiredPreviewWidth +
747 ". mDesiredPreviewHeight=" + mDesiredPreviewHeight);
748 }
749
Angus Kong5f8c30e2014-03-06 17:15:08 -0800750 @TargetApi(Build.VERSION_CODES.HONEYCOMB)
751 /**
752 * Calculates the preview size and stores it in mDesiredPreviewWidth and
753 * mDesiredPreviewHeight. This function checks {@link
754 * android.hardware.Camera.Parameters#getPreferredPreviewSizeForVideo()}
755 * but also considers the current preview area size on screen and make sure
756 * the final preview size will not be smaller than 1/2 of the current
757 * on screen preview area in terms of their short sides.
758 *
759 * @return The preferred preview size or {@code null} if the camera is not
760 * opened yet.
761 */
762 private static Point getDesiredPreviewSize(Context context, Parameters parameters,
763 CamcorderProfile profile, Point previewScreenSize) {
764 if (parameters.getSupportedVideoSizes() == null) {
765 // Driver doesn't support separate outputs for preview and video.
766 return new Point(profile.videoFrameWidth, profile.videoFrameHeight);
767 }
768
769 final int previewScreenShortSide = (previewScreenSize.x < previewScreenSize.y ?
770 previewScreenSize.x : previewScreenSize.y);
771 List<Size> sizes = parameters.getSupportedPreviewSizes();
772 Size preferred = parameters.getPreferredPreviewSizeForVideo();
773 final int preferredPreviewSizeShortSide = (preferred.width < preferred.height ?
774 preferred.width : preferred.height);
775 if (preferredPreviewSizeShortSide * 2 < previewScreenShortSide) {
776 preferred.width = profile.videoFrameWidth;
777 preferred.height = profile.videoFrameHeight;
778 }
779 int product = preferred.width * preferred.height;
780 Iterator<Size> it = sizes.iterator();
781 // Remove the preview sizes that are not preferred.
782 while (it.hasNext()) {
783 Size size = it.next();
784 if (size.width * size.height > product) {
785 it.remove();
786 }
787 }
788 Size optimalSize = CameraUtil.getOptimalPreviewSize(context, sizes,
789 (double) profile.videoFrameWidth / profile.videoFrameHeight);
790 return new Point(optimalSize.width, optimalSize.height);
791 }
792
Michael Kolb8872c232013-01-29 10:33:22 -0800793 private void resizeForPreviewAspectRatio() {
Doris Liue038c162013-12-13 23:06:11 -0800794 mUI.setAspectRatio((float) mProfile.videoFrameWidth / mProfile.videoFrameHeight);
Michael Kolb8872c232013-01-29 10:33:22 -0800795 }
796
Angus Kong13e87c42013-11-25 10:02:47 -0800797 private void installIntentFilter() {
Michael Kolb8872c232013-01-29 10:33:22 -0800798 // install an intent filter to receive SD card related events.
799 IntentFilter intentFilter =
800 new IntentFilter(Intent.ACTION_MEDIA_EJECT);
801 intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
802 intentFilter.addDataScheme("file");
803 mReceiver = new MyBroadcastReceiver();
804 mActivity.registerReceiver(mReceiver, intentFilter);
805 }
806
Michael Kolb8872c232013-01-29 10:33:22 -0800807 private void setDisplayOrientation() {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700808 mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
809 mCameraDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId);
Doris Liu6432cd62013-06-13 17:20:31 -0700810 // Change the camera display orientation
811 if (mCameraDevice != null) {
812 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800813 }
Doris Liua1ec04a2014-01-13 17:29:40 -0800814 if (mFocusManager != null) {
815 mFocusManager.setDisplayOrientation(mCameraDisplayOrientation);
816 }
Doris Liu6432cd62013-06-13 17:20:31 -0700817 }
818
819 @Override
820 public void updateCameraOrientation() {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100821 if (mMediaRecorderRecording) {
822 return;
823 }
Angus Kongb50b5cb2013-08-09 14:55:20 -0700824 if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
Doris Liu6432cd62013-06-13 17:20:31 -0700825 setDisplayOrientation();
826 }
Michael Kolb8872c232013-01-29 10:33:22 -0800827 }
828
Doris Liu6827ce22013-03-12 19:24:28 -0700829 @Override
Doris Liu70da9182013-12-17 18:41:15 -0800830 public void updatePreviewAspectRatio(float aspectRatio) {
831 mAppController.updatePreviewAspectRatio(aspectRatio);
832 }
833
834 @Override
Doris Liu6827ce22013-03-12 19:24:28 -0700835 public int onZoomChanged(int index) {
836 // Not useful to change zoom value when the activity is paused.
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100837 if (mPaused) {
838 return index;
839 }
Doris Liu6827ce22013-03-12 19:24:28 -0700840 mZoomValue = index;
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100841 if (mParameters == null || mCameraDevice == null) {
842 return index;
843 }
Doris Liu6827ce22013-03-12 19:24:28 -0700844 // Set zoom parameters asynchronously
845 mParameters.setZoom(mZoomValue);
Doris Liu6432cd62013-06-13 17:20:31 -0700846 mCameraDevice.setParameters(mParameters);
847 Parameters p = mCameraDevice.getParameters();
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100848 if (p != null) {
849 return p.getZoom();
850 }
Doris Liu6827ce22013-03-12 19:24:28 -0700851 return index;
852 }
Angus Kong395ee2d2013-07-15 12:42:41 -0700853
Michael Kolb8872c232013-01-29 10:33:22 -0800854 private void startPreview() {
Alan Newbergerd41766f2014-04-09 18:25:34 -0700855 Log.i(TAG, "startPreview");
Michael Kolb8872c232013-01-29 10:33:22 -0800856
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800857 SurfaceTexture surfaceTexture = mActivity.getCameraAppUI().getSurfaceTexture();
ztenghuief01a312013-10-14 15:25:16 -0700858 if (!mPreferenceRead || surfaceTexture == null || mPaused == true ||
859 mCameraDevice == null) {
860 return;
861 }
Angus Kong395ee2d2013-07-15 12:42:41 -0700862
Angus Kong5596b4c2014-03-11 16:27:30 -0700863 mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
Michael Kolb8872c232013-01-29 10:33:22 -0800864 if (mPreviewing == true) {
865 stopPreview();
Michael Kolb8872c232013-01-29 10:33:22 -0800866 }
867
Michael Kolb8872c232013-01-29 10:33:22 -0800868 setDisplayOrientation();
Doris Liu6432cd62013-06-13 17:20:31 -0700869 mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation);
Michael Kolb8872c232013-01-29 10:33:22 -0800870 setCameraParameters();
871
Doris Liua1ec04a2014-01-13 17:29:40 -0800872 if (mFocusManager != null) {
873 // If the focus mode is continuous autofocus, call cancelAutoFocus
874 // to resume it because it may have been paused by autoFocus call.
875 String focusMode = mFocusManager.getFocusMode();
876 if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
877 mCameraDevice.cancelAutoFocus();
878 }
879 }
Doris Liu5a367542014-01-17 17:21:42 -0800880
881 // This is to notify app controller that preview will start next, so app
882 // controller can set preview callbacks if needed. This has to happen before
883 // preview is started as a workaround of the framework issue related to preview
884 // callbacks that causes preview stretch and crash. (More details see b/12210027
885 // and b/12591410
886 mAppController.onPreviewReadyToStart();
Michael Kolb8872c232013-01-29 10:33:22 -0800887 try {
Doris Liu3973deb2013-08-21 13:42:22 -0700888 mCameraDevice.setPreviewTexture(surfaceTexture);
889 mCameraDevice.startPreview();
890 mPreviewing = true;
891 onPreviewStarted();
Michael Kolb8872c232013-01-29 10:33:22 -0800892 } catch (Throwable ex) {
893 closeCamera();
894 throw new RuntimeException("startPreview failed", ex);
Michael Kolb8872c232013-01-29 10:33:22 -0800895 }
Michael Kolbb1aeb392013-03-11 12:37:40 -0700896 }
897
898 private void onPreviewStarted() {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700899 mAppController.setShutterEnabled(true);
Doris Liu2b906b82013-12-10 16:34:08 -0800900 mAppController.onPreviewStarted();
Doris Liua1ec04a2014-01-13 17:29:40 -0800901 if (mFocusManager != null) {
902 mFocusManager.onPreviewStarted();
903 }
Michael Kolb8872c232013-01-29 10:33:22 -0800904 }
905
Doris Liu6827ce22013-03-12 19:24:28 -0700906 @Override
907 public void stopPreview() {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100908 if (!mPreviewing) {
909 return;
910 }
Doris Liu6432cd62013-06-13 17:20:31 -0700911 mCameraDevice.stopPreview();
Doris Liua1ec04a2014-01-13 17:29:40 -0800912 if (mFocusManager != null) {
913 mFocusManager.onPreviewStopped();
914 }
Michael Kolb8872c232013-01-29 10:33:22 -0800915 mPreviewing = false;
916 }
917
Michael Kolb8872c232013-01-29 10:33:22 -0800918 private void closeCamera() {
Alan Newbergerd41766f2014-04-09 18:25:34 -0700919 Log.i(TAG, "closeCamera");
Doris Liu6432cd62013-06-13 17:20:31 -0700920 if (mCameraDevice == null) {
Michael Kolb8872c232013-01-29 10:33:22 -0800921 Log.d(TAG, "already stopped.");
922 return;
923 }
Doris Liu6432cd62013-06-13 17:20:31 -0700924 mCameraDevice.setZoomChangeListener(null);
Angus Kong5596b4c2014-03-11 16:27:30 -0700925 mCameraDevice.setErrorCallback(null, null);
Angus Kong20fad242013-11-11 18:23:46 -0800926 mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
Angus Kong395ee2d2013-07-15 12:42:41 -0700927 mCameraDevice = null;
Michael Kolb8872c232013-01-29 10:33:22 -0800928 mPreviewing = false;
929 mSnapshotInProgress = false;
Doris Liua1ec04a2014-01-13 17:29:40 -0800930 if (mFocusManager != null) {
931 mFocusManager.onCameraReleased();
932 }
Michael Kolb8872c232013-01-29 10:33:22 -0800933 }
934
Michael Kolb8872c232013-01-29 10:33:22 -0800935 @Override
Michael Kolb8872c232013-01-29 10:33:22 -0800936 public boolean onBackPressed() {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +0100937 if (mPaused) {
938 return true;
939 }
Michael Kolb8872c232013-01-29 10:33:22 -0800940 if (mMediaRecorderRecording) {
941 onStopVideoRecording();
942 return true;
Michael Kolb8872c232013-01-29 10:33:22 -0800943 } else {
Doris Liuf9e4f8f2013-12-04 18:04:22 -0800944 return false;
Michael Kolb8872c232013-01-29 10:33:22 -0800945 }
946 }
947
948 @Override
949 public boolean onKeyDown(int keyCode, KeyEvent event) {
950 // Do not handle any key if the activity is paused.
951 if (mPaused) {
952 return true;
953 }
954
955 switch (keyCode) {
956 case KeyEvent.KEYCODE_CAMERA:
957 if (event.getRepeatCount() == 0) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700958 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -0800959 return true;
960 }
Michael Kolb8872c232013-01-29 10:33:22 -0800961 case KeyEvent.KEYCODE_DPAD_CENTER:
962 if (event.getRepeatCount() == 0) {
Erin Dahlgren667630d2014-04-01 14:03:25 -0700963 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -0800964 return true;
965 }
Michael Kolb8872c232013-01-29 10:33:22 -0800966 case KeyEvent.KEYCODE_MENU:
Erin Dahlgren15691af2014-03-14 14:10:57 -0700967 // Consume menu button presses during capture.
968 return mMediaRecorderRecording;
Michael Kolb8872c232013-01-29 10:33:22 -0800969 }
970 return false;
971 }
972
973 @Override
974 public boolean onKeyUp(int keyCode, KeyEvent event) {
975 switch (keyCode) {
976 case KeyEvent.KEYCODE_CAMERA:
Erin Dahlgren667630d2014-04-01 14:03:25 -0700977 onShutterButtonClick();
Michael Kolb8872c232013-01-29 10:33:22 -0800978 return true;
Erin Dahlgren15691af2014-03-14 14:10:57 -0700979 case KeyEvent.KEYCODE_MENU:
980 // Consume menu button presses during capture.
981 return mMediaRecorderRecording;
Michael Kolb8872c232013-01-29 10:33:22 -0800982 }
983 return false;
984 }
985
Doris Liu6827ce22013-03-12 19:24:28 -0700986 @Override
987 public boolean isVideoCaptureIntent() {
Michael Kolb8872c232013-01-29 10:33:22 -0800988 String action = mActivity.getIntent().getAction();
989 return (MediaStore.ACTION_VIDEO_CAPTURE.equals(action));
990 }
991
992 private void doReturnToCaller(boolean valid) {
993 Intent resultIntent = new Intent();
994 int resultCode;
995 if (valid) {
996 resultCode = Activity.RESULT_OK;
997 resultIntent.setData(mCurrentVideoUri);
998 } else {
999 resultCode = Activity.RESULT_CANCELED;
1000 }
1001 mActivity.setResultEx(resultCode, resultIntent);
1002 mActivity.finish();
1003 }
1004
1005 private void cleanupEmptyFile() {
1006 if (mVideoFilename != null) {
1007 File f = new File(mVideoFilename);
1008 if (f.length() == 0 && f.delete()) {
1009 Log.v(TAG, "Empty video file deleted: " + mVideoFilename);
1010 mVideoFilename = null;
1011 }
1012 }
1013 }
1014
Michael Kolb8872c232013-01-29 10:33:22 -08001015 // Prepares media recorder.
1016 private void initializeRecorder() {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001017 Log.i(TAG, "initializeRecorder");
Michael Kolb8872c232013-01-29 10:33:22 -08001018 // If the mCameraDevice is null, then this activity is going to finish
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001019 if (mCameraDevice == null) {
1020 return;
1021 }
Michael Kolb8872c232013-01-29 10:33:22 -08001022
Michael Kolb8872c232013-01-29 10:33:22 -08001023 Intent intent = mActivity.getIntent();
1024 Bundle myExtras = intent.getExtras();
1025
1026 long requestedSizeLimit = 0;
1027 closeVideoFileDescriptor();
ztenghui70bd0242013-10-14 11:15:44 -07001028 mCurrentVideoUriFromMediaSaved = false;
Michael Kolb8872c232013-01-29 10:33:22 -08001029 if (mIsVideoCaptureIntent && myExtras != null) {
1030 Uri saveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
1031 if (saveUri != null) {
1032 try {
1033 mVideoFileDescriptor =
1034 mContentResolver.openFileDescriptor(saveUri, "rw");
1035 mCurrentVideoUri = saveUri;
1036 } catch (java.io.FileNotFoundException ex) {
1037 // invalid uri
1038 Log.e(TAG, ex.toString());
1039 }
1040 }
1041 requestedSizeLimit = myExtras.getLong(MediaStore.EXTRA_SIZE_LIMIT);
1042 }
1043 mMediaRecorder = new MediaRecorder();
1044
Michael Kolb8872c232013-01-29 10:33:22 -08001045 // Unlock the camera object before passing it to media recorder.
Doris Liu6432cd62013-06-13 17:20:31 -07001046 mCameraDevice.unlock();
Doris Liu6432cd62013-06-13 17:20:31 -07001047 mMediaRecorder.setCamera(mCameraDevice.getCamera());
Michael Kolb8872c232013-01-29 10:33:22 -08001048 if (!mCaptureTimeLapse) {
1049 mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
1050 }
1051 mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
1052 mMediaRecorder.setProfile(mProfile);
Michael Kolbf9542362013-05-30 07:45:41 -07001053 mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight);
Michael Kolb8872c232013-01-29 10:33:22 -08001054 mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
1055 if (mCaptureTimeLapse) {
1056 double fps = 1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs;
1057 setCaptureRate(mMediaRecorder, fps);
1058 }
1059
1060 setRecordLocation();
1061
1062 // Set output file.
1063 // Try Uri in the intent first. If it doesn't exist, use our own
1064 // instead.
1065 if (mVideoFileDescriptor != null) {
1066 mMediaRecorder.setOutputFile(mVideoFileDescriptor.getFileDescriptor());
1067 } else {
1068 generateVideoFilename(mProfile.fileFormat);
1069 mMediaRecorder.setOutputFile(mVideoFilename);
1070 }
1071
1072 // Set maximum file size.
Angus Kong2dcc0a92013-09-25 13:00:08 -07001073 long maxFileSize = mActivity.getStorageSpaceBytes() - Storage.LOW_STORAGE_THRESHOLD_BYTES;
Michael Kolb8872c232013-01-29 10:33:22 -08001074 if (requestedSizeLimit > 0 && requestedSizeLimit < maxFileSize) {
1075 maxFileSize = requestedSizeLimit;
1076 }
1077
1078 try {
1079 mMediaRecorder.setMaxFileSize(maxFileSize);
1080 } catch (RuntimeException exception) {
1081 // We are going to ignore failure of setMaxFileSize here, as
1082 // a) The composer selected may simply not support it, or
1083 // b) The underlying media framework may not handle 64-bit range
1084 // on the size restriction.
1085 }
1086
1087 // See android.hardware.Camera.Parameters.setRotation for
1088 // documentation.
1089 // Note that mOrientation here is the device orientation, which is the opposite of
1090 // what activity.getWindowManager().getDefaultDisplay().getRotation() would return,
1091 // which is the orientation the graphics need to rotate in order to render correctly.
1092 int rotation = 0;
1093 if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
Angus Kong20fad242013-11-11 18:23:46 -08001094 CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001095 if (isCameraFrontFacing()) {
Michael Kolb8872c232013-01-29 10:33:22 -08001096 rotation = (info.orientation - mOrientation + 360) % 360;
1097 } else { // back-facing camera
1098 rotation = (info.orientation + mOrientation) % 360;
1099 }
1100 }
1101 mMediaRecorder.setOrientationHint(rotation);
1102
1103 try {
1104 mMediaRecorder.prepare();
1105 } catch (IOException e) {
1106 Log.e(TAG, "prepare failed for " + mVideoFilename, e);
1107 releaseMediaRecorder();
1108 throw new RuntimeException(e);
1109 }
1110
1111 mMediaRecorder.setOnErrorListener(this);
1112 mMediaRecorder.setOnInfoListener(this);
1113 }
1114
Michael Kolb8872c232013-01-29 10:33:22 -08001115 private static void setCaptureRate(MediaRecorder recorder, double fps) {
1116 recorder.setCaptureRate(fps);
1117 }
1118
Michael Kolb8872c232013-01-29 10:33:22 -08001119 private void setRecordLocation() {
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001120 Location loc = mLocationManager.getCurrentLocation();
1121 if (loc != null) {
1122 mMediaRecorder.setLocation((float) loc.getLatitude(),
1123 (float) loc.getLongitude());
Michael Kolb8872c232013-01-29 10:33:22 -08001124 }
1125 }
1126
Michael Kolb8872c232013-01-29 10:33:22 -08001127 private void releaseMediaRecorder() {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001128 Log.i(TAG, "Releasing media recorder.");
Michael Kolb8872c232013-01-29 10:33:22 -08001129 if (mMediaRecorder != null) {
1130 cleanupEmptyFile();
1131 mMediaRecorder.reset();
1132 mMediaRecorder.release();
1133 mMediaRecorder = null;
1134 }
1135 mVideoFilename = null;
1136 }
1137
Michael Kolb8872c232013-01-29 10:33:22 -08001138 private void generateVideoFilename(int outputFileFormat) {
1139 long dateTaken = System.currentTimeMillis();
1140 String title = createName(dateTaken);
1141 // Used when emailing.
1142 String filename = title + convertOutputFormatToFileExt(outputFileFormat);
1143 String mime = convertOutputFormatToMimeType(outputFileFormat);
1144 String path = Storage.DIRECTORY + '/' + filename;
1145 String tmpPath = path + ".tmp";
Ruben Brunk16007962013-04-19 15:27:57 -07001146 mCurrentVideoValues = new ContentValues(9);
Michael Kolb8872c232013-01-29 10:33:22 -08001147 mCurrentVideoValues.put(Video.Media.TITLE, title);
1148 mCurrentVideoValues.put(Video.Media.DISPLAY_NAME, filename);
1149 mCurrentVideoValues.put(Video.Media.DATE_TAKEN, dateTaken);
Ruben Brunk16007962013-04-19 15:27:57 -07001150 mCurrentVideoValues.put(MediaColumns.DATE_MODIFIED, dateTaken / 1000);
Michael Kolb8872c232013-01-29 10:33:22 -08001151 mCurrentVideoValues.put(Video.Media.MIME_TYPE, mime);
1152 mCurrentVideoValues.put(Video.Media.DATA, path);
Sam Juddde3e9ab2014-03-17 13:07:22 -07001153 mCurrentVideoValues.put(Video.Media.WIDTH, mProfile.videoFrameWidth);
1154 mCurrentVideoValues.put(Video.Media.HEIGHT, mProfile.videoFrameHeight);
Michael Kolb8872c232013-01-29 10:33:22 -08001155 mCurrentVideoValues.put(Video.Media.RESOLUTION,
1156 Integer.toString(mProfile.videoFrameWidth) + "x" +
1157 Integer.toString(mProfile.videoFrameHeight));
1158 Location loc = mLocationManager.getCurrentLocation();
1159 if (loc != null) {
1160 mCurrentVideoValues.put(Video.Media.LATITUDE, loc.getLatitude());
1161 mCurrentVideoValues.put(Video.Media.LONGITUDE, loc.getLongitude());
1162 }
Michael Kolb8872c232013-01-29 10:33:22 -08001163 mVideoFilename = tmpPath;
1164 Log.v(TAG, "New video filename: " + mVideoFilename);
1165 }
1166
Angus Kong83a99ae2013-04-17 15:37:07 -07001167 private void saveVideo() {
Michael Kolb8872c232013-01-29 10:33:22 -08001168 if (mVideoFileDescriptor == null) {
Michael Kolb8872c232013-01-29 10:33:22 -08001169 long duration = SystemClock.uptimeMillis() - mRecordingStartTime;
1170 if (duration > 0) {
1171 if (mCaptureTimeLapse) {
1172 duration = getTimeLapseVideoLength(duration);
1173 }
Michael Kolb8872c232013-01-29 10:33:22 -08001174 } else {
1175 Log.w(TAG, "Video duration <= 0 : " + duration);
1176 }
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001177 getServices().getMediaSaver().addVideo(mCurrentVideoFilename,
Andy Huibers10c58162014-03-29 14:06:54 -07001178 duration, isCameraFrontFacing(), mCurrentVideoValues,
Angus Kong83a99ae2013-04-17 15:37:07 -07001179 mOnVideoSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001180 }
1181 mCurrentVideoValues = null;
Michael Kolb8872c232013-01-29 10:33:22 -08001182 }
1183
1184 private void deleteVideoFile(String fileName) {
1185 Log.v(TAG, "Deleting video " + fileName);
1186 File f = new File(fileName);
1187 if (!f.delete()) {
1188 Log.v(TAG, "Could not delete " + fileName);
1189 }
1190 }
1191
Michael Kolb8872c232013-01-29 10:33:22 -08001192 // from MediaRecorder.OnErrorListener
1193 @Override
1194 public void onError(MediaRecorder mr, int what, int extra) {
1195 Log.e(TAG, "MediaRecorder error. what=" + what + ". extra=" + extra);
1196 if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) {
1197 // We may have run out of space on the sdcard.
1198 stopVideoRecording();
1199 mActivity.updateStorageSpaceAndHint();
1200 }
1201 }
1202
1203 // from MediaRecorder.OnInfoListener
1204 @Override
1205 public void onInfo(MediaRecorder mr, int what, int extra) {
1206 if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001207 if (mMediaRecorderRecording) {
1208 onStopVideoRecording();
1209 }
Michael Kolb8872c232013-01-29 10:33:22 -08001210 } else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001211 if (mMediaRecorderRecording) {
1212 onStopVideoRecording();
1213 }
Michael Kolb8872c232013-01-29 10:33:22 -08001214
1215 // Show the toast.
1216 Toast.makeText(mActivity, R.string.video_reach_size_limit,
1217 Toast.LENGTH_LONG).show();
1218 }
1219 }
1220
1221 /*
1222 * Make sure we're not recording music playing in the background, ask the
1223 * MediaPlaybackService to pause playback.
1224 */
1225 private void pauseAudioPlayback() {
Marco Nelissen20694b22013-10-29 15:27:24 -07001226 AudioManager am = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE);
1227 am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
Michael Kolb8872c232013-01-29 10:33:22 -08001228 }
1229
1230 // For testing.
1231 public boolean isRecording() {
1232 return mMediaRecorderRecording;
1233 }
1234
1235 private void startVideoRecording() {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001236 Log.i(TAG, "startVideoRecording");
Sascha Haeberling37f36112013-08-06 14:31:52 -07001237 mUI.cancelAnimations();
Doris Liu6432cd62013-06-13 17:20:31 -07001238 mUI.setSwipingEnabled(false);
Doris Liu38c6bc32014-01-16 18:03:18 -08001239 mUI.showFocusUI(false);
Doris Liud6487c92014-02-28 10:35:45 -08001240 mUI.showVideoRecordingHints(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001241
1242 mActivity.updateStorageSpaceAndHint();
Angus Kong2dcc0a92013-09-25 13:00:08 -07001243 if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001244 Log.w(TAG, "Storage issue, ignore the start request");
Michael Kolb8872c232013-01-29 10:33:22 -08001245 return;
1246 }
1247
Angus Kong9ef99252013-07-18 18:04:19 -07001248 //??
1249 //if (!mCameraDevice.waitDone()) return;
Michael Kolb8872c232013-01-29 10:33:22 -08001250 mCurrentVideoUri = null;
Doris Liu3973deb2013-08-21 13:42:22 -07001251
1252 initializeRecorder();
1253 if (mMediaRecorder == null) {
1254 Log.e(TAG, "Fail to initialize media recorder");
1255 return;
Michael Kolb8872c232013-01-29 10:33:22 -08001256 }
1257
1258 pauseAudioPlayback();
1259
Doris Liu3973deb2013-08-21 13:42:22 -07001260 try {
1261 mMediaRecorder.start(); // Recording is now started
1262 } catch (RuntimeException e) {
1263 Log.e(TAG, "Could not start media recorder. ", e);
1264 releaseMediaRecorder();
1265 // If start fails, frameworks will not lock the camera for us.
1266 mCameraDevice.lock();
1267 return;
Michael Kolb8872c232013-01-29 10:33:22 -08001268 }
Sascha Haeberling8793eff2014-01-15 16:33:59 -08001269 mAppController.getCameraAppUI().setSwipeEnabled(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001270
Angus Kong104e0012013-04-03 16:56:18 -07001271 // The parameters might have been altered by MediaRecorder already.
1272 // We need to force mCameraDevice to refresh before getting it.
Doris Liu6432cd62013-06-13 17:20:31 -07001273 mCameraDevice.refreshParameters();
Michael Kolb8872c232013-01-29 10:33:22 -08001274 // The parameters may have been changed by MediaRecorder upon starting
1275 // recording. We need to alter the parameters if we support camcorder
1276 // zoom. To reduce latency when setting the parameters during zoom, we
1277 // update mParameters here once.
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001278 mParameters = mCameraDevice.getParameters();
Michael Kolb8872c232013-01-29 10:33:22 -08001279
Michael Kolb8872c232013-01-29 10:33:22 -08001280 mMediaRecorderRecording = true;
Angus Kong9f1db522013-11-09 16:25:59 -08001281 mActivity.lockOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001282 mRecordingStartTime = SystemClock.uptimeMillis();
Doris Liu4df91582014-03-21 18:33:57 -07001283
1284 // A special case of mode options closing: during capture it should
1285 // not be possible to change mode state.
1286 mAppController.getCameraAppUI().hideModeOptions();
1287 mAppController.getCameraAppUI().animateBottomBarToVideoStop(R.drawable.ic_stop);
Doris Liufe6596c2013-10-08 11:03:37 -07001288 mUI.showRecordingUI(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001289
Doris Liua1ec04a2014-01-13 17:29:40 -08001290 setFocusParameters();
Michael Kolb8872c232013-01-29 10:33:22 -08001291 updateRecordingTime();
Angus Kong13e87c42013-11-25 10:02:47 -08001292 mActivity.enableKeepScreenOn(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001293 }
1294
Sascha Haeberling37f36112013-08-06 14:31:52 -07001295 private Bitmap getVideoThumbnail() {
Michael Kolb8872c232013-01-29 10:33:22 -08001296 Bitmap bitmap = null;
1297 if (mVideoFileDescriptor != null) {
1298 bitmap = Thumbnail.createVideoThumbnailBitmap(mVideoFileDescriptor.getFileDescriptor(),
Doris Liu3c2fca32013-02-13 18:28:03 -08001299 mDesiredPreviewWidth);
Doris Liu2a7f44c2013-08-12 15:18:53 -07001300 } else if (mCurrentVideoUri != null) {
1301 try {
1302 mVideoFileDescriptor = mContentResolver.openFileDescriptor(mCurrentVideoUri, "r");
1303 bitmap = Thumbnail.createVideoThumbnailBitmap(
1304 mVideoFileDescriptor.getFileDescriptor(), mDesiredPreviewWidth);
1305 } catch (java.io.FileNotFoundException ex) {
1306 // invalid uri
1307 Log.e(TAG, ex.toString());
1308 }
Michael Kolb8872c232013-01-29 10:33:22 -08001309 }
Doris Liu3973deb2013-08-21 13:42:22 -07001310
Michael Kolb8872c232013-01-29 10:33:22 -08001311 if (bitmap != null) {
1312 // MetadataRetriever already rotates the thumbnail. We should rotate
1313 // it to match the UI orientation (and mirror if it is front-facing camera).
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001314 bitmap = CameraUtil.rotateAndMirror(bitmap, 0, isCameraFrontFacing());
Sascha Haeberling37f36112013-08-06 14:31:52 -07001315 }
1316 return bitmap;
1317 }
1318
1319 private void showCaptureResult() {
1320 mIsInReviewMode = true;
1321 Bitmap bitmap = getVideoThumbnail();
1322 if (bitmap != null) {
Doris Liu6827ce22013-03-12 19:24:28 -07001323 mUI.showReviewImage(bitmap);
Michael Kolb8872c232013-01-29 10:33:22 -08001324 }
Doris Liu6827ce22013-03-12 19:24:28 -07001325 mUI.showReviewControls();
Doris Liu6827ce22013-03-12 19:24:28 -07001326 mUI.showTimeLapseUI(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001327 }
1328
Michael Kolb8872c232013-01-29 10:33:22 -08001329 private boolean stopVideoRecording() {
Alan Newbergerd41766f2014-04-09 18:25:34 -07001330 Log.i(TAG, "stopVideoRecording");
Doris Liu6432cd62013-06-13 17:20:31 -07001331 mUI.setSwipingEnabled(true);
Doris Liu38c6bc32014-01-16 18:03:18 -08001332 mUI.showFocusUI(true);
Doris Liud6487c92014-02-28 10:35:45 -08001333 mUI.showVideoRecordingHints(true);
Michael Kolb8872c232013-01-29 10:33:22 -08001334
1335 boolean fail = false;
1336 if (mMediaRecorderRecording) {
1337 boolean shouldAddToMediaStoreNow = false;
1338
1339 try {
Doris Liu3973deb2013-08-21 13:42:22 -07001340 mMediaRecorder.setOnErrorListener(null);
1341 mMediaRecorder.setOnInfoListener(null);
1342 mMediaRecorder.stop();
1343 shouldAddToMediaStoreNow = true;
Michael Kolb8872c232013-01-29 10:33:22 -08001344 mCurrentVideoFilename = mVideoFilename;
Andy Huibers10c58162014-03-29 14:06:54 -07001345 Log.v(TAG, "stopVideoRecording: current video filename: " + mCurrentVideoFilename);
Michael Kolb8872c232013-01-29 10:33:22 -08001346 } catch (RuntimeException e) {
1347 Log.e(TAG, "stop fail", e);
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001348 if (mVideoFilename != null) {
1349 deleteVideoFile(mVideoFilename);
1350 }
Michael Kolb8872c232013-01-29 10:33:22 -08001351 fail = true;
1352 }
1353 mMediaRecorderRecording = false;
Angus Kong9f1db522013-11-09 16:25:59 -08001354 mActivity.unlockOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001355
1356 // If the activity is paused, this means activity is interrupted
1357 // during recording. Release the camera as soon as possible because
1358 // face unlock or other applications may need to use the camera.
Michael Kolb8872c232013-01-29 10:33:22 -08001359 if (mPaused) {
Doris Liu3973deb2013-08-21 13:42:22 -07001360 closeCamera();
Michael Kolb8872c232013-01-29 10:33:22 -08001361 }
1362
Doris Liufe6596c2013-10-08 11:03:37 -07001363 mUI.showRecordingUI(false);
Michael Kolb8872c232013-01-29 10:33:22 -08001364 // The orientation was fixed during video recording. Now make it
1365 // reflect the device orientation as video recording is stopped.
Doris Liu6827ce22013-03-12 19:24:28 -07001366 mUI.setOrientationIndicator(0, true);
Angus Kong13e87c42013-11-25 10:02:47 -08001367 mActivity.enableKeepScreenOn(false);
Doris Liu2a7f44c2013-08-12 15:18:53 -07001368 if (shouldAddToMediaStoreNow && !fail) {
1369 if (mVideoFileDescriptor == null) {
1370 saveVideo();
1371 } else if (mIsVideoCaptureIntent) {
1372 // if no file save is needed, we can show the post capture UI now
1373 showCaptureResult();
1374 }
Michael Kolb8872c232013-01-29 10:33:22 -08001375 }
1376 }
Doris Liu3973deb2013-08-21 13:42:22 -07001377 // release media recorder
1378 releaseMediaRecorder();
Doris Liu4df91582014-03-21 18:33:57 -07001379
1380 mAppController.getCameraAppUI().showModeOptions();
1381 mAppController.getCameraAppUI().animateBottomBarToFullSize(mShutterIconId);
Doris Liu3973deb2013-08-21 13:42:22 -07001382 if (!mPaused) {
Doris Liu1143ebd2014-01-24 14:11:50 -08001383 setFocusParameters();
Doris Liu3973deb2013-08-21 13:42:22 -07001384 mCameraDevice.lock();
1385 if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) {
1386 stopPreview();
Doris Liu3973deb2013-08-21 13:42:22 -07001387 // Switch back to use SurfaceTexture for preview.
1388 startPreview();
Michael Kolb8872c232013-01-29 10:33:22 -08001389 }
Angus Kong62753ae2014-02-10 10:53:54 -08001390 // Update the parameters here because the parameters might have been altered
1391 // by MediaRecorder.
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001392 mParameters = mCameraDevice.getParameters();
1393 }
Michael Kolb8872c232013-01-29 10:33:22 -08001394 return fail;
1395 }
1396
Michael Kolb8872c232013-01-29 10:33:22 -08001397 private static String millisecondToTimeString(long milliSeconds, boolean displayCentiSeconds) {
1398 long seconds = milliSeconds / 1000; // round down to compute seconds
1399 long minutes = seconds / 60;
1400 long hours = minutes / 60;
1401 long remainderMinutes = minutes - (hours * 60);
1402 long remainderSeconds = seconds - (minutes * 60);
1403
1404 StringBuilder timeStringBuilder = new StringBuilder();
1405
1406 // Hours
1407 if (hours > 0) {
1408 if (hours < 10) {
1409 timeStringBuilder.append('0');
1410 }
1411 timeStringBuilder.append(hours);
1412
1413 timeStringBuilder.append(':');
1414 }
1415
1416 // Minutes
1417 if (remainderMinutes < 10) {
1418 timeStringBuilder.append('0');
1419 }
1420 timeStringBuilder.append(remainderMinutes);
1421 timeStringBuilder.append(':');
1422
1423 // Seconds
1424 if (remainderSeconds < 10) {
1425 timeStringBuilder.append('0');
1426 }
1427 timeStringBuilder.append(remainderSeconds);
1428
1429 // Centi seconds
1430 if (displayCentiSeconds) {
1431 timeStringBuilder.append('.');
1432 long remainderCentiSeconds = (milliSeconds - seconds * 1000) / 10;
1433 if (remainderCentiSeconds < 10) {
1434 timeStringBuilder.append('0');
1435 }
1436 timeStringBuilder.append(remainderCentiSeconds);
1437 }
1438
1439 return timeStringBuilder.toString();
1440 }
1441
1442 private long getTimeLapseVideoLength(long deltaMs) {
1443 // For better approximation calculate fractional number of frames captured.
1444 // This will update the video time at a higher resolution.
1445 double numberOfFrames = (double) deltaMs / mTimeBetweenTimeLapseFrameCaptureMs;
1446 return (long) (numberOfFrames / mProfile.videoFrameRate * 1000);
1447 }
1448
1449 private void updateRecordingTime() {
1450 if (!mMediaRecorderRecording) {
1451 return;
1452 }
1453 long now = SystemClock.uptimeMillis();
1454 long delta = now - mRecordingStartTime;
1455
1456 // Starting a minute before reaching the max duration
1457 // limit, we'll countdown the remaining time instead.
1458 boolean countdownRemainingTime = (mMaxVideoDurationInMs != 0
1459 && delta >= mMaxVideoDurationInMs - 60000);
1460
1461 long deltaAdjusted = delta;
1462 if (countdownRemainingTime) {
1463 deltaAdjusted = Math.max(0, mMaxVideoDurationInMs - deltaAdjusted) + 999;
1464 }
1465 String text;
1466
1467 long targetNextUpdateDelay;
1468 if (!mCaptureTimeLapse) {
1469 text = millisecondToTimeString(deltaAdjusted, false);
1470 targetNextUpdateDelay = 1000;
1471 } else {
1472 // The length of time lapse video is different from the length
1473 // of the actual wall clock time elapsed. Display the video length
1474 // only in format hh:mm:ss.dd, where dd are the centi seconds.
1475 text = millisecondToTimeString(getTimeLapseVideoLength(delta), true);
1476 targetNextUpdateDelay = mTimeBetweenTimeLapseFrameCaptureMs;
1477 }
1478
Doris Liu6827ce22013-03-12 19:24:28 -07001479 mUI.setRecordingTime(text);
Michael Kolb8872c232013-01-29 10:33:22 -08001480
1481 if (mRecordingTimeCountsDown != countdownRemainingTime) {
1482 // Avoid setting the color on every update, do it only
1483 // when it needs changing.
1484 mRecordingTimeCountsDown = countdownRemainingTime;
1485
1486 int color = mActivity.getResources().getColor(countdownRemainingTime
1487 ? R.color.recording_time_remaining_text
1488 : R.color.recording_time_elapsed_text);
1489
Doris Liu6827ce22013-03-12 19:24:28 -07001490 mUI.setRecordingTimeTextColor(color);
Michael Kolb8872c232013-01-29 10:33:22 -08001491 }
1492
1493 long actualNextUpdateDelay = targetNextUpdateDelay - (delta % targetNextUpdateDelay);
Angus Kong13e87c42013-11-25 10:02:47 -08001494 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_RECORD_TIME, actualNextUpdateDelay);
Michael Kolb8872c232013-01-29 10:33:22 -08001495 }
1496
1497 private static boolean isSupported(String value, List<String> supported) {
1498 return supported == null ? false : supported.indexOf(value) >= 0;
1499 }
1500
1501 @SuppressWarnings("deprecation")
1502 private void setCameraParameters() {
Erin Dahlgrenbd3da262013-12-02 11:48:13 -08001503 SettingsManager settingsManager = mActivity.getSettingsManager();
1504
Michael Kolb8872c232013-01-29 10:33:22 -08001505 mParameters.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
Andy Huibers6dcf90c2014-04-14 13:53:12 -07001506 // This is required for Samsung SGH-I337 and probably other Samsung S4 versions
1507 if (Build.BRAND.toLowerCase().contains("samsung")) {
1508 mParameters.set("video-size", mProfile.videoFrameWidth + "x" + mProfile.videoFrameHeight);
1509 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001510 int[] fpsRange = CameraUtil.getMaxPreviewFpsRange(mParameters);
Doris Liu6432cd62013-06-13 17:20:31 -07001511 if (fpsRange.length > 0) {
1512 mParameters.setPreviewFpsRange(
1513 fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX],
1514 fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]);
1515 } else {
1516 mParameters.setPreviewFrameRate(mProfile.videoFrameRate);
1517 }
Michael Kolb8872c232013-01-29 10:33:22 -08001518
Erin Dahlgrenfce8a0b2013-12-12 17:36:32 -08001519 enableTorchMode(settingsManager.isCameraBackFacing());
Michael Kolb8872c232013-01-29 10:33:22 -08001520
Michael Kolb8872c232013-01-29 10:33:22 -08001521 // Set zoom.
1522 if (mParameters.isZoomSupported()) {
1523 mParameters.setZoom(mZoomValue);
1524 }
Doris Liua1ec04a2014-01-13 17:29:40 -08001525 updateFocusParameters();
Michael Kolb8872c232013-01-29 10:33:22 -08001526
Angus Kongb50b5cb2013-08-09 14:55:20 -07001527 mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.TRUE);
Michael Kolb8872c232013-01-29 10:33:22 -08001528
1529 // Enable video stabilization. Convenience methods not available in API
1530 // level <= 14
1531 String vstabSupported = mParameters.get("video-stabilization-supported");
1532 if ("true".equals(vstabSupported)) {
1533 mParameters.set("video-stabilization", "true");
1534 }
1535
1536 // Set picture size.
1537 // The logic here is different from the logic in still-mode camera.
1538 // There we determine the preview size based on the picture size, but
1539 // here we determine the picture size based on the preview size.
1540 List<Size> supported = mParameters.getSupportedPictureSizes();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001541 Size optimalSize = CameraUtil.getOptimalVideoSnapshotPictureSize(supported,
Michael Kolb8872c232013-01-29 10:33:22 -08001542 (double) mDesiredPreviewWidth / mDesiredPreviewHeight);
1543 Size original = mParameters.getPictureSize();
1544 if (!original.equals(optimalSize)) {
1545 mParameters.setPictureSize(optimalSize.width, optimalSize.height);
1546 }
Alan Newbergerd41766f2014-04-09 18:25:34 -07001547 Log.d(TAG, "Video snapshot size is " + optimalSize.width + "x" +
Michael Kolb8872c232013-01-29 10:33:22 -08001548 optimalSize.height);
1549
1550 // Set JPEG quality.
1551 int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId,
1552 CameraProfile.QUALITY_HIGH);
1553 mParameters.setJpegQuality(jpegQuality);
1554
Doris Liu6432cd62013-06-13 17:20:31 -07001555 mCameraDevice.setParameters(mParameters);
Andy Huibers64abfe92014-01-14 22:25:52 -08001556 // Nexus 5 through KitKat 4.4.2 requires a second call to
1557 // .setParameters() for frame rate settings to take effect.
1558 mCameraDevice.setParameters(mParameters);
Sascha Haeberlingf8b877c2013-09-09 17:32:48 -07001559
1560 // Update UI based on the new parameters.
Erin Dahlgrenbd3da262013-12-02 11:48:13 -08001561 mUI.updateOnScreenIndicators(mParameters);
Michael Kolb8872c232013-01-29 10:33:22 -08001562 }
1563
Doris Liua1ec04a2014-01-13 17:29:40 -08001564 private void updateFocusParameters() {
1565 // Set continuous autofocus. During recording, we use "continuous-video"
1566 // auto focus mode to ensure smooth focusing. Whereas during preview (i.e.
1567 // before recording starts) we use "continuous-picture" auto focus mode
1568 // for faster but slightly jittery focusing.
1569 List<String> supportedFocus = mParameters.getSupportedFocusModes();
1570 if (mMediaRecorderRecording) {
1571 if (isSupported(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, supportedFocus)) {
1572 mParameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
1573 mFocusManager.overrideFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
1574 } else {
1575 mFocusManager.overrideFocusMode(null);
1576 }
1577 } else {
1578 mFocusManager.overrideFocusMode(null);
1579 if (isSupported(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE, supportedFocus)) {
1580 mParameters.setFocusMode(mFocusManager.getFocusMode());
1581 if (mFocusAreaSupported) {
1582 mParameters.setFocusAreas(mFocusManager.getFocusAreas());
1583 }
1584 }
1585 }
1586 updateAutoFocusMoveCallback();
1587 }
1588
Michael Kolb8872c232013-01-29 10:33:22 -08001589 @Override
Angus Kong20fad242013-11-11 18:23:46 -08001590 public void resume() {
Spike Sprague51c877c2014-02-18 11:14:12 -08001591 if (isVideoCaptureIntent()) {
1592 mDontResetIntentUiOnResume = mPaused;
1593 }
1594
Angus Kongc4e66562013-11-22 23:03:21 -08001595 mPaused = false;
Angus Kong13e87c42013-11-25 10:02:47 -08001596 installIntentFilter();
Erin Dahlgren667630d2014-04-01 14:03:25 -07001597 mAppController.setShutterEnabled(false);
Angus Kongc4e66562013-11-22 23:03:21 -08001598 mZoomValue = 0;
1599
1600 showVideoSnapshotUI(false);
1601
1602 if (!mPreviewing) {
1603 requestCamera(mCameraId);
1604 } else {
1605 // preview already started
Erin Dahlgren667630d2014-04-01 14:03:25 -07001606 mAppController.setShutterEnabled(true);
Angus Kongc4e66562013-11-22 23:03:21 -08001607 }
1608
Doris Liua1ec04a2014-01-13 17:29:40 -08001609 if (mFocusManager != null) {
1610 // If camera is not open when resume is called, focus manager will not
1611 // be initialized yet, in which case it will start listening to
1612 // preview area size change later in the initialization.
1613 mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
1614 }
Sascha Haeberlingde303232014-02-07 02:30:53 +01001615
Angus Kongc4e66562013-11-22 23:03:21 -08001616 if (mPreviewing) {
1617 mOnResumeTime = SystemClock.uptimeMillis();
Angus Kong13e87c42013-11-25 10:02:47 -08001618 mHandler.sendEmptyMessageDelayed(MSG_CHECK_DISPLAY_ROTATION, 100);
Angus Kongc4e66562013-11-22 23:03:21 -08001619 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001620 getServices().getMemoryManager().addListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001621 }
1622
1623 @Override
1624 public void pause() {
Angus Kongc4e66562013-11-22 23:03:21 -08001625 mPaused = true;
Doris Liua1ec04a2014-01-13 17:29:40 -08001626
1627 if (mFocusManager != null) {
1628 // If camera is not open when resume is called, focus manager will not
1629 // be initialized yet, in which case it will start listening to
1630 // preview area size change later in the initialization.
1631 mAppController.removePreviewAreaSizeChangedListener(mFocusManager);
1632 mFocusManager.removeMessages();
1633 }
Angus Kongc4e66562013-11-22 23:03:21 -08001634 if (mMediaRecorderRecording) {
1635 // Camera will be released in onStopVideoRecording.
1636 onStopVideoRecording();
1637 } else {
1638 stopPreview();
1639 closeCamera();
1640 releaseMediaRecorder();
1641 }
1642
1643 closeVideoFileDescriptor();
Angus Kongc4e66562013-11-22 23:03:21 -08001644
1645 if (mReceiver != null) {
1646 mActivity.unregisterReceiver(mReceiver);
1647 mReceiver = null;
1648 }
Angus Kongc4e66562013-11-22 23:03:21 -08001649
Angus Kong13e87c42013-11-25 10:02:47 -08001650 mHandler.removeMessages(MSG_CHECK_DISPLAY_ROTATION);
1651 mHandler.removeMessages(MSG_SWITCH_CAMERA);
1652 mHandler.removeMessages(MSG_SWITCH_CAMERA_START_ANIMATION);
Angus Kongc4e66562013-11-22 23:03:21 -08001653 mPendingSwitchCameraId = -1;
1654 mSwitchingCamera = false;
1655 mPreferenceRead = false;
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001656 getServices().getMemoryManager().removeListener(this);
Angus Kong20fad242013-11-11 18:23:46 -08001657 }
1658
1659 @Override
1660 public void destroy() {
1661
1662 }
1663
1664 @Override
Angus Kong2f0e4a32013-12-03 10:02:35 -08001665 public void onLayoutOrientationChanged(boolean isLandscape) {
Michael Kolb8872c232013-01-29 10:33:22 -08001666 setDisplayOrientation();
Michael Kolb8872c232013-01-29 10:33:22 -08001667 }
1668
Erin Dahlgrena07e94c2013-12-04 18:44:08 -08001669 // TODO: integrate this into the SettingsManager listeners.
Michael Kolb8872c232013-01-29 10:33:22 -08001670 public void onSharedPreferenceChanged() {
Michael Kolb8872c232013-01-29 10:33:22 -08001671
Michael Kolb8872c232013-01-29 10:33:22 -08001672 }
1673
1674 private void switchCamera() {
Sascha Haeberlingf8b877c2013-09-09 17:32:48 -07001675 if (mPaused) {
1676 return;
1677 }
Erin Dahlgrenbd3da262013-12-02 11:48:13 -08001678 SettingsManager settingsManager = mActivity.getSettingsManager();
Michael Kolb8872c232013-01-29 10:33:22 -08001679
1680 Log.d(TAG, "Start to switch camera.");
1681 mCameraId = mPendingSwitchCameraId;
1682 mPendingSwitchCameraId = -1;
Erin Dahlgrenbd3da262013-12-02 11:48:13 -08001683 settingsManager.set(SettingsManager.SETTING_CAMERA_ID, "" + mCameraId);
Michael Kolb8872c232013-01-29 10:33:22 -08001684
Doris Liua1ec04a2014-01-13 17:29:40 -08001685 if (mFocusManager != null) {
1686 mFocusManager.removeMessages();
1687 }
Michael Kolb8872c232013-01-29 10:33:22 -08001688 closeCamera();
Angus Kong20fad242013-11-11 18:23:46 -08001689 requestCamera(mCameraId);
Michael Kolb8872c232013-01-29 10:33:22 -08001690
Sascha Haeberling6ccec202014-03-11 09:44:34 -07001691 mMirror = isCameraFrontFacing();
Doris Liua1ec04a2014-01-13 17:29:40 -08001692 if (mFocusManager != null) {
1693 mFocusManager.setMirror(mMirror);
1694 }
1695
Michael Kolb8872c232013-01-29 10:33:22 -08001696 // From onResume
Doris Liu6432cd62013-06-13 17:20:31 -07001697 mZoomValue = 0;
Doris Liu6827ce22013-03-12 19:24:28 -07001698 mUI.setOrientationIndicator(0, false);
Michael Kolb8872c232013-01-29 10:33:22 -08001699
Doris Liu6432cd62013-06-13 17:20:31 -07001700 // Start switch camera animation. Post a message because
1701 // onFrameAvailable from the old camera may already exist.
Angus Kong13e87c42013-11-25 10:02:47 -08001702 mHandler.sendEmptyMessage(MSG_SWITCH_CAMERA_START_ANIMATION);
Erin Dahlgrenbd3da262013-12-02 11:48:13 -08001703 mUI.updateOnScreenIndicators(mParameters);
Michael Kolb8872c232013-01-29 10:33:22 -08001704 }
1705
Michael Kolb8872c232013-01-29 10:33:22 -08001706 private void initializeVideoSnapshot() {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001707 if (mParameters == null) {
1708 return;
1709 }
Michael Kolb8872c232013-01-29 10:33:22 -08001710 }
1711
1712 void showVideoSnapshotUI(boolean enabled) {
Sascha Haeberling846d3ab2014-02-04 12:48:55 +01001713 if (mParameters == null) {
1714 return;
1715 }
Angus Kongb50b5cb2013-08-09 14:55:20 -07001716 if (CameraUtil.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) {
Doris Liu6432cd62013-06-13 17:20:31 -07001717 if (enabled) {
Sascha Haeberling37f36112013-08-06 14:31:52 -07001718 mUI.animateFlash();
Michael Kolb8872c232013-01-29 10:33:22 -08001719 } else {
Doris Liu6827ce22013-03-12 19:24:28 -07001720 mUI.showPreviewBorder(enabled);
Michael Kolb8872c232013-01-29 10:33:22 -08001721 }
Erin Dahlgren667630d2014-04-01 14:03:25 -07001722 mAppController.setShutterEnabled(!enabled);
Michael Kolb8872c232013-01-29 10:33:22 -08001723 }
1724 }
1725
Erin Dahlgrenfce8a0b2013-12-12 17:36:32 -08001726 /**
1727 * Used to update the flash mode. Video mode can turn on the flash as torch
1728 * mode, which we would like to turn on and off when we switching in and
1729 * out to the preview.
1730 *
1731 * @param enable Whether torch mode can be enabled.
1732 */
1733 private void enableTorchMode(boolean enable) {
1734 if (mParameters.getFlashMode() == null) {
1735 return;
1736 }
1737
Erin Dahlgrenbd3da262013-12-02 11:48:13 -08001738 SettingsManager settingsManager = mActivity.getSettingsManager();
1739
ztenghui7b265a62013-09-09 14:58:44 -07001740 String flashMode;
Erin Dahlgrenfce8a0b2013-12-12 17:36:32 -08001741 if (enable) {
Erin Dahlgrenbd3da262013-12-02 11:48:13 -08001742 flashMode = settingsManager.get(SettingsManager.SETTING_VIDEOCAMERA_FLASH_MODE);
ztenghui7b265a62013-09-09 14:58:44 -07001743 } else {
1744 flashMode = Parameters.FLASH_MODE_OFF;
1745 }
1746 List<String> supportedFlash = mParameters.getSupportedFlashModes();
1747 if (isSupported(flashMode, supportedFlash)) {
1748 mParameters.setFlashMode(flashMode);
1749 } else {
1750 flashMode = mParameters.getFlashMode();
1751 if (flashMode == null) {
1752 flashMode = mActivity.getString(
1753 R.string.pref_camera_flashmode_no_flash);
Angus Kongfaaee012013-12-07 00:38:46 -08001754 mParameters.setFlashMode(flashMode);
Michael Kolb8872c232013-01-29 10:33:22 -08001755 }
Michael Kolb8872c232013-01-29 10:33:22 -08001756 }
ztenghui7b265a62013-09-09 14:58:44 -07001757 mCameraDevice.setParameters(mParameters);
Erin Dahlgrenbd3da262013-12-02 11:48:13 -08001758 mUI.updateOnScreenIndicators(mParameters);
ztenghui7b265a62013-09-09 14:58:44 -07001759 }
1760
Michael Kolb8872c232013-01-29 10:33:22 -08001761 @Override
Sascha Haeberling8c1a9222014-02-25 09:38:06 -08001762 public void onPreviewVisibilityChanged(int visibility) {
Erin Dahlgrenfce8a0b2013-12-12 17:36:32 -08001763 if (mPreviewing) {
Sascha Haeberling8c1a9222014-02-25 09:38:06 -08001764 enableTorchMode(visibility == ModuleController.VISIBILITY_VISIBLE);
Erin Dahlgrenfce8a0b2013-12-12 17:36:32 -08001765 }
Erin Dahlgren3044d8c2013-10-10 18:23:45 -07001766 }
1767
Angus Kong9ef99252013-07-18 18:04:19 -07001768 private final class JpegPictureCallback implements CameraPictureCallback {
Michael Kolb8872c232013-01-29 10:33:22 -08001769 Location mLocation;
1770
1771 public JpegPictureCallback(Location loc) {
1772 mLocation = loc;
1773 }
1774
1775 @Override
Angus Kong9ef99252013-07-18 18:04:19 -07001776 public void onPictureTaken(byte [] jpegData, CameraProxy camera) {
Doris Liu057b21a2014-04-25 17:35:27 -07001777 Log.i(TAG, "Video snapshot taken.");
Michael Kolb8872c232013-01-29 10:33:22 -08001778 mSnapshotInProgress = false;
1779 showVideoSnapshotUI(false);
1780 storeImage(jpegData, mLocation);
1781 }
1782 }
1783
1784 private void storeImage(final byte[] data, Location loc) {
1785 long dateTaken = System.currentTimeMillis();
Angus Kongb50b5cb2013-08-09 14:55:20 -07001786 String title = CameraUtil.createJpegName(dateTaken);
Angus Kong0d00a892013-03-26 11:40:40 -07001787 ExifInterface exif = Exif.getExif(data);
1788 int orientation = Exif.getOrientation(exif);
Doris Liu6df2d962013-08-20 16:31:29 -07001789
Andy Huibers10c58162014-03-29 14:06:54 -07001790 int zoomIndex = mParameters.getZoom();
1791 float zoomValue = 0.01f * mParameters.getZoomRatios().get(zoomIndex);
1792 UsageStatistics.instance().photoCaptureDoneEvent(
1793 eventprotos.NavigationChange.Mode.VIDEO_STILL, title + ".jpeg", exif,
1794 isCameraFrontFacing(), false, zoomValue);
1795
Sascha Haeberling280fd3e2013-11-21 13:52:15 -08001796 getServices().getMediaSaver().addImage(
Doris Liu6df2d962013-08-20 16:31:29 -07001797 data, title, dateTaken, loc, orientation,
Angus Kong83a99ae2013-04-17 15:37:07 -07001798 exif, mOnPhotoSavedListener, mContentResolver);
Michael Kolb8872c232013-01-29 10:33:22 -08001799 }
1800
Michael Kolb8872c232013-01-29 10:33:22 -08001801 private String convertOutputFormatToMimeType(int outputFileFormat) {
1802 if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) {
1803 return "video/mp4";
1804 }
1805 return "video/3gpp";
1806 }
1807
1808 private String convertOutputFormatToFileExt(int outputFileFormat) {
1809 if (outputFileFormat == MediaRecorder.OutputFormat.MPEG_4) {
1810 return ".mp4";
1811 }
1812 return ".3gp";
1813 }
1814
1815 private void closeVideoFileDescriptor() {
1816 if (mVideoFileDescriptor != null) {
1817 try {
1818 mVideoFileDescriptor.close();
1819 } catch (IOException e) {
1820 Log.e(TAG, "Fail to close fd", e);
1821 }
1822 mVideoFileDescriptor = null;
1823 }
1824 }
1825
Michael Kolb8872c232013-01-29 10:33:22 -08001826 @Override
Angus Kong395ee2d2013-07-15 12:42:41 -07001827 public void onPreviewUIReady() {
1828 startPreview();
1829 }
1830
1831 @Override
1832 public void onPreviewUIDestroyed() {
1833 stopPreview();
1834 }
Angus Kong20fad242013-11-11 18:23:46 -08001835
Doris Liu1dfe7822013-12-12 00:02:08 -08001836 @Override
1837 public void startPreCaptureAnimation() {
1838 mAppController.startPreCaptureAnimation();
1839 }
1840
Angus Kong20fad242013-11-11 18:23:46 -08001841 private void requestCamera(int id) {
1842 mActivity.getCameraProvider().requestCamera(id);
1843 }
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001844
1845 @Override
1846 public void onMemoryStateChanged(int state) {
Erin Dahlgren667630d2014-04-01 14:03:25 -07001847 mAppController.setShutterEnabled(state == MemoryManager.STATE_OK);
Sascha Haeberlinga63dbb62013-11-22 11:55:32 -08001848 }
1849
1850 @Override
1851 public void onLowMemory() {
1852 // Not much we can do in the video module.
1853 }
1854
Doris Liua1ec04a2014-01-13 17:29:40 -08001855 /***********************FocusOverlayManager Listener****************************/
1856 @Override
1857 public void autoFocus() {
1858 mCameraDevice.autoFocus(mHandler, mAutoFocusCallback);
1859 }
1860
1861 @Override
1862 public void cancelAutoFocus() {
1863 mCameraDevice.cancelAutoFocus();
1864 setFocusParameters();
1865 }
1866
1867 @Override
1868 public boolean capture() {
1869 return false;
1870 }
1871
1872 @Override
1873 public void startFaceDetection() {
1874
1875 }
1876
1877 @Override
1878 public void stopFaceDetection() {
1879
1880 }
1881
1882 @Override
1883 public void setFocusParameters() {
1884 updateFocusParameters();
1885 mCameraDevice.setParameters(mParameters);
1886 }
1887
Michael Kolb8872c232013-01-29 10:33:22 -08001888}