import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.provider.MediaStore;
import android.provider.MediaStore.MediaColumns;
import android.provider.MediaStore.Video;
-import android.util.Log;
import android.view.KeyEvent;
import android.view.OrientationEventListener;
import android.view.View;
import com.android.camera.app.MediaSaver;
import com.android.camera.app.MemoryManager;
import com.android.camera.app.MemoryManager.MemoryListener;
+import com.android.camera.debug.Log;
import com.android.camera.exif.ExifInterface;
import com.android.camera.hardware.HardwareSpec;
import com.android.camera.hardware.HardwareSpecImpl;
import com.android.camera.module.ModuleController;
import com.android.camera.settings.SettingsManager;
import com.android.camera.settings.SettingsUtil;
-import com.android.camera.util.AccessibilityUtils;
import com.android.camera.util.ApiHelper;
import com.android.camera.util.CameraUtil;
import com.android.camera.util.UsageStatistics;
implements ModuleController,
VideoController,
MemoryListener,
- ShutterButton.OnShutterButtonListener,
MediaRecorder.OnErrorListener,
MediaRecorder.OnInfoListener, FocusOverlayManager.Listener {
- private static final String TAG = "VideoModule";
+ private static final Log.Tag TAG = new Log.Tag("VideoModule");
// Messages defined for the UI thread handler.
private static final int MSG_CHECK_DISPLAY_ROTATION = 4;
// module fields
private CameraActivity mActivity;
private boolean mPaused;
+
+ // if, during and intent capture, the activity is paused (e.g. when app switching or reviewing a
+ // shot video), we don't want the bottom bar intent ui to reset to the capture button
+ private boolean mDontResetIntentUiOnResume;
+
private int mCameraId;
private Parameters mParameters;
private int mMaxVideoDurationInMs;
// Time Lapse parameters.
- private boolean mCaptureTimeLapse = false;
+ private final boolean mCaptureTimeLapse = false;
// Default 0. If it is larger than 0, the camcorder is in time lapse mode.
- private int mTimeBetweenTimeLapseFrameCaptureMs = 0;
+ private final int mTimeBetweenTimeLapseFrameCaptureMs = 0;
boolean mPreviewing = false; // True if preview is started.
// The display rotation in degrees. This is only valid when mPreviewing is
mAppController = mActivity;
mUI = new VideoUI(mActivity, this, mActivity.getModuleLayoutRoot());
mActivity.setPreviewStatusListener(mUI);
- mActivity.getCameraAppUI().setBottomBarShutterListener(this);
SettingsManager settingsManager = mActivity.getSettingsManager();
mCameraId = Integer.parseInt(settingsManager.get(SettingsManager.SETTING_CAMERA_ID));
private void initializeControlByIntent() {
if (isVideoCaptureIntent()) {
- mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
+ if (!mDontResetIntentUiOnResume) {
+ mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
+ }
+ // reset the flag
+ mDontResetIntentUiOnResume = false;
}
}
@Override
public void onSingleTapUp(View view, int x, int y) {
- if (mPaused || mCameraDevice == null) {
+ if (mMediaRecorderRecording || mPaused || mCameraDevice == null) {
return;
}
// Check if metering area or focus area is supported.
}
// Set rotation and gps data.
- int rotation = CameraUtil.getJpegRotation(mActivity, mCameraId, mOrientation);
+ CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
+ int rotation = CameraUtil.getJpegRotation(info, mOrientation);
mParameters.setRotation(rotation);
Location loc = mLocationManager.getCurrentLocation();
CameraUtil.setGpsParameters(mParameters, loc);
null, null, null, new JpegPictureCallback(loc));
showVideoSnapshotUI(true);
mSnapshotInProgress = true;
- UsageStatistics.captureEvent(eventprotos.NavigationChange.Mode.VIDEO_STILL,
- null, null);
+ UsageStatistics.instance().captureEvent(
+ eventprotos.NavigationChange.Mode.VIDEO_STILL, null, null, null);
}
}
}
/**
+ * @return Whether the currently active camera is front-facing.
+ */
+ private boolean isCameraFrontFacing() {
+ CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
+ return info.facing == CameraInfo.CAMERA_FACING_FRONT;
+ }
+
+ /**
* The focus manager gets initialized after camera is available.
*/
private void initializeFocusManager() {
if (mFocusManager != null) {
mFocusManager.removeMessages();
} else {
- CameraInfo info = mAppController.getCameraProvider().getCameraInfo()[mCameraId];
- mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
+ mMirror = isCameraFrontFacing();
String[] defaultFocusModes = mActivity.getResources().getStringArray(
R.array.pref_camera_focusmode_default_array);
mFocusManager = new FocusOverlayManager(mActivity.getSettingsManager(),
new ButtonManager.ButtonCallback() {
@Override
public void onStateChanged(int state) {
- if (mPaused || mPendingSwitchCameraId != -1) {
+ if (mPaused || mAppController.getCameraProvider().waitingForCamera()) {
return;
}
mPendingSwitchCameraId = state;
private final View.OnClickListener mReviewCallback = new View.OnClickListener() {
@Override
public void onClick(View v) {
- mActivity.getCameraAppUI().transitionToIntentCaptureLayout();
onReviewPlayClicked(v);
}
};
@Override
+ public void hardResetSettings(SettingsManager settingsManager) {
+ // VideoModule does not need to hard reset any settings.
+ }
+
+ @Override
public HardwareSpec getHardwareSpec() {
return (mParameters != null ? new HardwareSpecImpl(mParameters) : null);
}
bottomBarSpec.enableTorchFlash = true;
bottomBarSpec.flashCallback = mFlashCallback;
bottomBarSpec.hideHdr = true;
- bottomBarSpec.hideGridLines = true;
+ bottomBarSpec.enableGridLines = true;
if (isVideoCaptureIntent()) {
bottomBarSpec.showCancel = true;
// The preference stores values from ListPreference and is thus string type for all values.
// We need to convert it to int manually.
SettingsManager settingsManager = mActivity.getSettingsManager();
- if (!settingsManager.isSet(SettingsManager.SETTING_VIDEO_QUALITY)) {
- settingsManager.setDefault(SettingsManager.SETTING_VIDEO_QUALITY);
+ if (!settingsManager.isSet(SettingsManager.SETTING_VIDEO_QUALITY_BACK)) {
+ settingsManager.setDefault(SettingsManager.SETTING_VIDEO_QUALITY_BACK);
+ }
+ if (!settingsManager.isSet(SettingsManager.SETTING_VIDEO_QUALITY_FRONT)) {
+ settingsManager.setDefault(SettingsManager.SETTING_VIDEO_QUALITY_FRONT);
}
- String videoQuality = settingsManager.get(SettingsManager.SETTING_VIDEO_QUALITY);
+ String videoQuality = settingsManager
+ .get(isCameraFrontFacing() ? SettingsManager.SETTING_VIDEO_QUALITY_FRONT
+ : SettingsManager.SETTING_VIDEO_QUALITY_BACK);
int quality = SettingsUtil.getVideoQuality(videoQuality, mCameraId);
Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality);
mMaxVideoDurationInMs = CameraSettings.getMaxVideoDuration(mActivity);
}
- // Read time lapse recording interval.
+ // TODO: Uncomment this block to re-enable time-lapse.
+ /* // Read time lapse recording interval.
String frameIntervalStr = settingsManager.get(
SettingsManager.SETTING_VIDEO_TIME_LAPSE_FRAME_INTERVAL);
mTimeBetweenTimeLapseFrameCaptureMs = Integer.parseInt(frameIntervalStr);
// TODO: This should be checked instead directly +1000.
if (mCaptureTimeLapse) {
quality += 1000;
- }
+ } */
// If quality is not supported, request QUALITY_HIGH which is always supported.
if (CamcorderProfile.hasProfile(mCameraId, quality) == false) {
quality = CamcorderProfile.QUALITY_HIGH;
}
mProfile = CamcorderProfile.get(mCameraId, quality);
- getDesiredPreviewSize();
mPreferenceRead = true;
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- private void getDesiredPreviewSize() {
if (mCameraDevice == null) {
return;
}
mParameters = mCameraDevice.getParameters();
- if (mParameters.getSupportedVideoSizes() == null) {
- mDesiredPreviewWidth = mProfile.videoFrameWidth;
- mDesiredPreviewHeight = mProfile.videoFrameHeight;
- } else { // Driver supports separates outputs for preview and video.
- List<Size> sizes = mParameters.getSupportedPreviewSizes();
- Size preferred = mParameters.getPreferredPreviewSizeForVideo();
- int product = preferred.width * preferred.height;
- Iterator<Size> it = sizes.iterator();
- // Remove the preview sizes that are not preferred.
- while (it.hasNext()) {
- Size size = it.next();
- if (size.width * size.height > product) {
- it.remove();
- }
- }
- Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes,
- (double) mProfile.videoFrameWidth / mProfile.videoFrameHeight);
- mDesiredPreviewWidth = optimalSize.width;
- mDesiredPreviewHeight = optimalSize.height;
- }
+ Point desiredPreviewSize = getDesiredPreviewSize(mAppController.getAndroidContext(),
+ mParameters, mProfile, mUI.getPreviewScreenSize());
+ mDesiredPreviewWidth = desiredPreviewSize.x;
+ mDesiredPreviewHeight = desiredPreviewSize.y;
mUI.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
Log.v(TAG, "mDesiredPreviewWidth=" + mDesiredPreviewWidth +
". mDesiredPreviewHeight=" + mDesiredPreviewHeight);
}
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ /**
+ * Calculates the preview size and stores it in mDesiredPreviewWidth and
+ * mDesiredPreviewHeight. This function checks {@link
+ * android.hardware.Camera.Parameters#getPreferredPreviewSizeForVideo()}
+ * but also considers the current preview area size on screen and make sure
+ * the final preview size will not be smaller than 1/2 of the current
+ * on screen preview area in terms of their short sides.
+ *
+ * @return The preferred preview size or {@code null} if the camera is not
+ * opened yet.
+ */
+ private static Point getDesiredPreviewSize(Context context, Parameters parameters,
+ CamcorderProfile profile, Point previewScreenSize) {
+ if (parameters.getSupportedVideoSizes() == null) {
+ // Driver doesn't support separate outputs for preview and video.
+ return new Point(profile.videoFrameWidth, profile.videoFrameHeight);
+ }
+
+ final int previewScreenShortSide = (previewScreenSize.x < previewScreenSize.y ?
+ previewScreenSize.x : previewScreenSize.y);
+ List<Size> sizes = parameters.getSupportedPreviewSizes();
+ Size preferred = parameters.getPreferredPreviewSizeForVideo();
+ final int preferredPreviewSizeShortSide = (preferred.width < preferred.height ?
+ preferred.width : preferred.height);
+ if (preferredPreviewSizeShortSide * 2 < previewScreenShortSide) {
+ preferred.width = profile.videoFrameWidth;
+ preferred.height = profile.videoFrameHeight;
+ }
+ int product = preferred.width * preferred.height;
+ Iterator<Size> it = sizes.iterator();
+ // Remove the preview sizes that are not preferred.
+ while (it.hasNext()) {
+ Size size = it.next();
+ if (size.width * size.height > product) {
+ it.remove();
+ }
+ }
+ Size optimalSize = CameraUtil.getOptimalPreviewSize(context, sizes,
+ (double) profile.videoFrameWidth / profile.videoFrameHeight);
+ return new Point(optimalSize.width, optimalSize.height);
+ }
+
private void resizeForPreviewAspectRatio() {
mUI.setAspectRatio((float) mProfile.videoFrameWidth / mProfile.videoFrameHeight);
}
return;
}
- mCameraDevice.setErrorCallback(mErrorCallback);
+ mCameraDevice.setErrorCallback(mHandler, mErrorCallback);
if (mPreviewing == true) {
stopPreview();
}
return;
}
mCameraDevice.setZoomChangeListener(null);
- mCameraDevice.setErrorCallback(null);
+ mCameraDevice.setErrorCallback(null, null);
mActivity.getCameraProvider().releaseCamera(mCameraDevice.getCameraId());
mCameraDevice = null;
mPreviewing = false;
mUI.clickShutter();
return true;
}
- break;
case KeyEvent.KEYCODE_DPAD_CENTER:
if (event.getRepeatCount() == 0) {
mUI.clickShutter();
return true;
}
- break;
case KeyEvent.KEYCODE_MENU:
- if (mMediaRecorderRecording) {
- return true;
- }
- break;
+ // Consume menu button presses during capture.
+ return mMediaRecorderRecording;
}
return false;
}
case KeyEvent.KEYCODE_CAMERA:
mUI.pressShutter(false);
return true;
+ case KeyEvent.KEYCODE_MENU:
+ // Consume menu button presses during capture.
+ return mMediaRecorderRecording;
}
return false;
}
int rotation = 0;
if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) {
CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
- if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
+ if (isCameraFrontFacing()) {
rotation = (info.orientation - mOrientation + 360) % 360;
} else { // back-facing camera
rotation = (info.orientation + mOrientation) % 360;
mCurrentVideoValues.put(MediaColumns.DATE_MODIFIED, dateTaken / 1000);
mCurrentVideoValues.put(Video.Media.MIME_TYPE, mime);
mCurrentVideoValues.put(Video.Media.DATA, path);
+ mCurrentVideoValues.put(Video.Media.WIDTH, mProfile.videoFrameWidth);
+ mCurrentVideoValues.put(Video.Media.HEIGHT, mProfile.videoFrameHeight);
mCurrentVideoValues.put(Video.Media.RESOLUTION,
Integer.toString(mProfile.videoFrameWidth) + "x" +
Integer.toString(mProfile.videoFrameHeight));
}
}
- private PreferenceGroup filterPreferenceScreenByIntent(
- PreferenceGroup screen) {
- Intent intent = mActivity.getIntent();
- if (intent.hasExtra(MediaStore.EXTRA_VIDEO_QUALITY)) {
- CameraSettings.removePreferenceFromScreen(screen, CameraSettings.KEY_VIDEO_QUALITY);
- }
-
- if (intent.hasExtra(MediaStore.EXTRA_DURATION_LIMIT)) {
- CameraSettings.removePreferenceFromScreen(screen,
- CameraSettings.KEY_VIDEO_QUALITY);
- }
- return screen;
- }
-
// from MediaRecorder.OnErrorListener
@Override
public void onError(MediaRecorder mr, int what, int extra) {
mUI.cancelAnimations();
mUI.setSwipingEnabled(false);
mUI.showFocusUI(false);
-
- mAppController.getCameraAppUI().animateBottomBarToCircle(R.drawable.ic_stop);
+ mUI.showVideoRecordingHints(false);
mActivity.updateStorageSpaceAndHint();
if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
}
mAppController.getCameraAppUI().setSwipeEnabled(false);
- // Make sure the video recording has started before announcing
- // this in accessibility.
- AccessibilityUtils.makeAnnouncement(mUI.getShutterButton(),
- mActivity.getString(R.string.video_recording_started));
-
// The parameters might have been altered by MediaRecorder already.
// We need to force mCameraDevice to refresh before getting it.
mCameraDevice.refreshParameters();
mMediaRecorderRecording = true;
mActivity.lockOrientation();
mRecordingStartTime = SystemClock.uptimeMillis();
+
+ // A special case of mode options closing: during capture it should
+ // not be possible to change mode state.
+ mAppController.getCameraAppUI().hideModeOptions();
+ mAppController.getCameraAppUI().animateBottomBarToVideoStop(R.drawable.ic_stop);
mUI.showRecordingUI(true);
setFocusParameters();
if (bitmap != null) {
// MetadataRetriever already rotates the thumbnail. We should rotate
// it to match the UI orientation (and mirror if it is front-facing camera).
- CameraInfo[] info = mActivity.getCameraProvider().getCameraInfo();
- boolean mirror = (info[mCameraId].facing == CameraInfo.CAMERA_FACING_FRONT);
- bitmap = CameraUtil.rotateAndMirror(bitmap, 0, mirror);
+ bitmap = CameraUtil.rotateAndMirror(bitmap, 0, isCameraFrontFacing());
}
return bitmap;
}
Log.v(TAG, "stopVideoRecording");
mUI.setSwipingEnabled(true);
mUI.showFocusUI(true);
-
- mAppController.getCameraAppUI().animateBottomBarToFullSize(mShutterIconId);
+ mUI.showVideoRecordingHints(true);
boolean fail = false;
if (mMediaRecorderRecording) {
Log.v(TAG, "stopVideoRecording: Setting current video filename: "
+ mCurrentVideoFilename);
float duration = (SystemClock.uptimeMillis() - mRecordingStartTime) / 1000;
- UsageStatistics.captureEvent(eventprotos.NavigationChange.Mode.VIDEO_CAPTURE,
- mCurrentVideoFilename, mParameters, duration);
- AccessibilityUtils.makeAnnouncement(mUI.getShutterButton(),
- mActivity.getAndroidContext().getString(R.string
- .video_recording_stopped));
+ String statisticFilename = (mCurrentVideoFilename == null
+ ? "INTENT"
+ : mCurrentVideoFilename);
+ UsageStatistics.instance().captureEvent(
+ eventprotos.NavigationChange.Mode.VIDEO_CAPTURE, statisticFilename,
+ mParameters, duration);
} catch (RuntimeException e) {
Log.e(TAG, "stop fail", e);
if (mVideoFilename != null) {
}
// release media recorder
releaseMediaRecorder();
+
+ mAppController.getCameraAppUI().showModeOptions();
+ mAppController.getCameraAppUI().animateBottomBarToFullSize(mShutterIconId);
if (!mPaused) {
setFocusParameters();
mCameraDevice.lock();
// Switch back to use SurfaceTexture for preview.
startPreview();
}
- }
- // Update the parameters here because the parameters might have been altered
- // by MediaRecorder.
- if (!mPaused) {
+ // Update the parameters here because the parameters might have been altered
+ // by MediaRecorder.
mParameters = mCameraDevice.getParameters();
}
return fail;
SettingsManager settingsManager = mActivity.getSettingsManager();
mParameters.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
- mParameters.set("video-size", mProfile.videoFrameWidth+"x"+mProfile.videoFrameHeight);
int[] fpsRange = CameraUtil.getMaxPreviewFpsRange(mParameters);
if (fpsRange.length > 0) {
mParameters.setPreviewFpsRange(
enableTorchMode(settingsManager.isCameraBackFacing());
- // Set white balance parameter.
- String whiteBalance = settingsManager.get(SettingsManager.SETTING_WHITE_BALANCE);
- if (isSupported(whiteBalance,
- mParameters.getSupportedWhiteBalance())) {
- mParameters.setWhiteBalance(whiteBalance);
- } else {
- whiteBalance = mParameters.getWhiteBalance();
- if (whiteBalance == null) {
- whiteBalance = Parameters.WHITE_BALANCE_AUTO;
- }
- }
-
// Set zoom.
if (mParameters.isZoomSupported()) {
mParameters.setZoom(mZoomValue);
@Override
public void resume() {
+ if (isVideoCaptureIntent()) {
+ mDontResetIntentUiOnResume = mPaused;
+ }
+
mPaused = false;
installIntentFilter();
mUI.enableShutter(false);
mAppController.addPreviewAreaSizeChangedListener(mFocusManager);
}
- // Initialize location service.
- mActivity.syncLocationManagerSetting();
-
if (mPreviewing) {
mOnResumeTime = SystemClock.uptimeMillis();
mHandler.sendEmptyMessageDelayed(MSG_CHECK_DISPLAY_ROTATION, 100);
}
- UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.VIDEO_CAPTURE,
+ UsageStatistics.instance().changeScreen(eventprotos.NavigationChange.Mode.VIDEO_CAPTURE,
eventprotos.CameraEvent.InteractionCause.BUTTON);
getServices().getMemoryManager().addListener(this);
}
mReceiver = null;
}
- if (mLocationManager != null) {
- mLocationManager.recordLocation(false);
- }
-
mHandler.removeMessages(MSG_CHECK_DISPLAY_ROTATION);
mHandler.removeMessages(MSG_SWITCH_CAMERA);
mHandler.removeMessages(MSG_SWITCH_CAMERA_START_ANIMATION);
closeCamera();
requestCamera(mCameraId);
- CameraInfo info = mActivity.getCameraProvider().getCameraInfo()[mCameraId];
- mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
+ mMirror = isCameraFrontFacing();
if (mFocusManager != null) {
mFocusManager.setMirror(mMirror);
}
}
@Override
- public void onPreviewVisibilityChanged(boolean visible) {
+ public void onPreviewVisibilityChanged(int visibility) {
if (mPreviewing) {
- enableTorchMode(visible);
+ enableTorchMode(visibility == ModuleController.VISIBILITY_VISIBLE);
}
}