From 302dfef488297152978788c2341cd3460727b251 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 13 Nov 2016 23:27:35 -0800 Subject: snap: UI checkpoint #2 * Fix bugs from previous commit * Factor out recording controls * Clean up layouts * Asynchronous control for video module * Performance improvements Change-Id: I9d5833cecbd9280845b740ee8a3c47f023470625 --- src/com/android/camera/BaseUI.java | 29 +- src/com/android/camera/CameraActivity.java | 30 +- src/com/android/camera/CaptureUI.java | 4 +- src/com/android/camera/PhotoModule.java | 5 +- src/com/android/camera/PhotoUI.java | 13 +- src/com/android/camera/VideoController.java | 2 + src/com/android/camera/VideoModule.java | 932 +++++++++++---------- src/com/android/camera/VideoUI.java | 166 ++-- src/com/android/camera/WideAnglePanoramaUI.java | 6 +- src/com/android/camera/ui/CameraControls.java | 150 ++-- src/com/android/camera/ui/ModuleSwitcher.java | 2 +- src/com/android/camera/ui/RecordingTime.java | 298 +++++++ .../android/camera/ui/ReversibleLinearLayout.java | 57 ++ src/com/android/camera/util/CameraUtil.java | 14 +- 14 files changed, 1060 insertions(+), 648 deletions(-) create mode 100644 src/com/android/camera/ui/RecordingTime.java create mode 100644 src/com/android/camera/ui/ReversibleLinearLayout.java (limited to 'src/com') diff --git a/src/com/android/camera/BaseUI.java b/src/com/android/camera/BaseUI.java index 7fd3c1805..c5c3c2b8d 100644 --- a/src/com/android/camera/BaseUI.java +++ b/src/com/android/camera/BaseUI.java @@ -1,7 +1,10 @@ package com.android.camera; +import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.RectF; +import android.util.Log; +import android.util.Pair; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; @@ -20,13 +23,14 @@ import java.util.List; * toward an eventual unification - WF */ public abstract class BaseUI { - protected final CameraControls mCameraControls; protected final CaptureAnimationOverlay mCaptureOverlay; protected final View mPreviewCover; protected final CameraActivity mActivity; protected final ViewGroup mRootView; + protected CameraControls mCameraControls; + protected int mTopMargin = 0; protected int mBottomMargin = 0; protected int mScreenRatio = CameraUtil.RATIO_UNKNOWN; @@ -47,8 +51,10 @@ public abstract class BaseUI { Point size = new Point(); mActivity.getWindowManager().getDefaultDisplay().getRealSize(size); mScreenRatio = CameraUtil.determineRatio(size.x, size.y); - calculateMargins(size); - mCameraControls.setMargins(mTopMargin, mBottomMargin); + + Pair margins = CameraUtil.calculateMargins(mActivity); + mTopMargin = margins.first; + mBottomMargin = margins.second; } private void calculateMargins(Point size) { @@ -137,10 +143,25 @@ public abstract class BaseUI { if (mCaptureOverlay != null) { mCaptureOverlay.startFlashAnimation(shortFlash); } + mActivity.updateThumbnail(true); + } + + public void animateFlash(Bitmap bitmap) { + if (mCaptureOverlay != null) { + mCaptureOverlay.startFlashAnimation(false); + } + mActivity.updateThumbnail(bitmap); } protected void onPreviewRectChanged(RectF rect) { - mCaptureOverlay.setPreviewRect(rect); + CameraUtil.dumpRect(rect, "onPreviewRectChanged"); + + if (mCaptureOverlay != null) { + mCaptureOverlay.setPreviewRect(rect); + } + if (mCameraControls != null) { + mCameraControls.setPreviewRect(rect); + } } private void enableOverlays() { diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 3d630ecad..570fad1c4 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -580,23 +580,25 @@ public class CameraActivity extends Activity CameraActivity.this.setSystemBarsVisibility(visible); } - private boolean stripHasScrolled = false; + private float previewCoverAlpha = 1.0f; - @Override - public void onFilmStripScroll(int offset) { - if (offset == 0) { - if (stripHasScrolled) { - mCurrentModule.hidePreviewCover(); - mCurrentModule.setPreviewCoverAlpha(1.0f); - } - } else { - // preview cover becomes fully opaque when the film strip has - // scrolled half the width of the screen - float rangePx = mDisplayWidth / 2f; - mCurrentModule.setPreviewCoverAlpha((float)Math.min(1.0, offset/rangePx)); + private void setPreviewCoverAlpha(float alpha) { + if (alpha == previewCoverAlpha || alpha < 0.0f || alpha > 1.0f) { + return; + } + mCurrentModule.setPreviewCoverAlpha(alpha); + if (alpha == 0.0f) { + mCurrentModule.hidePreviewCover(); + } else if (previewCoverAlpha == 0.0f) { mCurrentModule.showPreviewCover(); - stripHasScrolled = true; } + previewCoverAlpha = alpha; + } + + @Override + public void onFilmStripScroll(int offset) { + float rangePx = mDisplayWidth / 2f; + setPreviewCoverAlpha((float)Math.min(1.0, offset/rangePx)); } }; diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index 81aa2b086..debd180c8 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -355,7 +355,6 @@ public class CaptureUI extends BaseUI implements PreviewGestures.SingleTapListen mRenderOverlay.requestLayout(); ((ViewGroup)mRootView).removeView(mRecordingTimeRect); - mCameraControls.setPreviewRatio(0, true); } public void onCameraOpened(List cameraIds) { @@ -399,7 +398,8 @@ public class CaptureUI extends BaseUI implements PreviewGestures.SingleTapListen mModule.onVideoButtonClick(); } }); - mCameraControls.setPreviewRatio(0, true); + + mCameraControls.disableMuteButton(); } public void initializeZoom(List ids) { diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index ef9c91f54..c4434bd1a 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -230,6 +230,7 @@ public class PhotoModule private int SCE_FACTOR_STEP = 10; private boolean mPreviewRestartSupport = false; + private boolean mPreviewStarting = false; // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true. private String mCropValue; @@ -2957,11 +2958,12 @@ public class PhotoModule /** This can run on a background thread, so don't do UI updates here. Post any view updates to MainHandler or do it on onPreviewStarted() . */ private void startPreview() { - if (mPaused || mCameraDevice == null || mParameters == null) { + if (mPaused || mCameraDevice == null || mParameters == null || mPreviewStarting) { return; } synchronized (mCameraDevice) { + mPreviewStarting = true; SurfaceHolder sh = null; Log.v(TAG, "startPreview: SurfaceHolder (MDP path)"); if (mUI != null) { @@ -3021,6 +3023,7 @@ public class PhotoModule Log.v(TAG, "Trigger snapshot from start preview."); mHandler.post(mDoSnapRunnable); } + mPreviewStarting = false; } @Override diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index 38c6497b6..1e019a6a8 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -252,8 +252,6 @@ public class PhotoUI extends BaseUI implements PieListener, mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); mMenuButton = mRootView.findViewById(R.id.menu); - mCameraControls.disableMuteButton(); - ViewStub faceViewStub = (ViewStub) mRootView .findViewById(R.id.face_view_stub); if (faceViewStub != null) { @@ -262,11 +260,8 @@ public class PhotoUI extends BaseUI implements PieListener, setSurfaceTextureSizeChangedListener(mFaceView); } mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring); - initIndicators(); mOrientationResize = false; mPrevOrientationResize = false; - - showFirstTimeHelp(); } public void setDownFactor(int factor) { @@ -305,7 +300,6 @@ public class PhotoUI extends BaseUI implements PieListener, mAspectRatioResize = true; mAspectRatio = ratio; } - mCameraControls.setPreviewRatio(mAspectRatio, false); layoutPreview(ratio); } @@ -580,8 +574,9 @@ public class PhotoUI extends BaseUI implements PieListener, // called from onResume but only the first time public void initializeFirstTime() { + initIndicators(); + // Initialize shutter button. - mShutterButton.setImageResource(R.drawable.shutter_button_anim); mShutterButton.setImageResource(R.drawable.btn_new_shutter); mShutterButton.setOnClickListener(new OnClickListener() { @Override @@ -596,6 +591,8 @@ public class PhotoUI extends BaseUI implements PieListener, mShutterButton.addOnShutterButtonListener(mController); mShutterButton.setVisibility(View.VISIBLE); + + mCameraControls.disableMuteButton(); } // called from onResume every other time @@ -634,7 +631,7 @@ public class PhotoUI extends BaseUI implements PieListener, public void updateOnScreenIndicators(Camera.Parameters params, PreferenceGroup group, ComboPreferences prefs) { - if (params == null || group == null) return; + if (params == null || group == null || mOnScreenIndicators == null) return; mOnScreenIndicators.updateSceneOnScreenIndicator(params.getSceneMode()); mOnScreenIndicators.updateExposureOnScreenIndicator(params, CameraSettings.readExposure(prefs)); diff --git a/src/com/android/camera/VideoController.java b/src/com/android/camera/VideoController.java index a5b92c1f4..7bb7bf202 100644 --- a/src/com/android/camera/VideoController.java +++ b/src/com/android/camera/VideoController.java @@ -16,6 +16,7 @@ package com.android.camera; +import android.graphics.Rect; import android.view.View; import com.android.camera.ShutterButton.OnShutterButtonListener; @@ -40,6 +41,7 @@ public interface VideoController extends OnShutterButtonListener, OnPauseButtonL // Callbacks for camera preview UI events. public void onPreviewUIReady(); public void onPreviewUIDestroyed(); + public void onPreviewRectChanged(Rect previewRect); public void onScreenSizeChanged(int width, int height); } diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index d6b3b7173..43fc7c584 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -30,6 +30,7 @@ import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.ImageFormat; +import android.graphics.Rect; import android.hardware.Camera; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; @@ -65,7 +66,6 @@ import android.media.EncoderCapabilities.VideoEncoderCap; import com.android.camera.CameraManager.CameraAFCallback; import com.android.camera.CameraManager.CameraPictureCallback; import com.android.camera.CameraManager.CameraProxy; -import com.android.camera.app.OrientationManager; import com.android.camera.exif.ExifInterface; import com.android.camera.ui.RotateImageView; import com.android.camera.ui.RotateTextToast; @@ -97,13 +97,21 @@ public class VideoModule implements CameraModule, private static final int CHECK_DISPLAY_ROTATION = 3; private static final int CLEAR_SCREEN_DELAY = 4; - private static final int UPDATE_RECORD_TIME = 5; private static final int ENABLE_SHUTTER_BUTTON = 6; private static final int SHOW_TAP_TO_SNAPSHOT_TOAST = 7; private static final int SWITCH_CAMERA = 8; private static final int SWITCH_CAMERA_START_ANIMATION = 9; private static final int HANDLE_FLASH_TORCH_DELAY = 10; private static final int SET_FOCUS_RATIO = 11; + private static final int CAMERA_OPEN_DONE = 12; + private static final int ON_PREVIEW_STARTED = 13; + private static final int SET_VIDEO_UI_PARAMS = 14; + + // The subset of parameters we need to update in setCameraParameters(). + private static final int UPDATE_PARAM_INITIALIZE = 1; + private static final int UPDATE_PARAM_ZOOM = 2; + private static final int UPDATE_PARAM_PREFERENCE = 4; + private static final int UPDATE_PARAM_ALL = -1; private static final int SCREEN_DELAY = 2 * 60 * 1000; @@ -127,6 +135,8 @@ public class VideoModule implements CameraModule, private boolean mPaused; private int mCameraId; private Parameters mParameters; + + private Parameters mInitialParams; private boolean mFocusAreaSupported; private boolean mMeteringAreaSupported; private boolean mAeLockSupported; @@ -141,6 +151,8 @@ public class VideoModule implements CameraModule, private PreferenceGroup mPreferenceGroup; private boolean mSaveToSDCard = false; + private boolean mCameraPreviewParamsReady = false; + // Preference must be read before starting preview. We check this before starting // preview. private boolean mPreferenceRead; @@ -153,9 +165,7 @@ public class VideoModule implements CameraModule, private boolean mSwitchingCamera; private boolean mMediaRecorderRecording = false; private boolean mMediaRecorderPausing = false; - private long mRecordingStartTime; - private long mRecordingTotalTime; - private boolean mRecordingTimeCountsDown = false; + private long mOnResumeTime; // The video file that the hardware camera is about to record into // (or is recording into.) @@ -196,7 +206,6 @@ public class VideoModule implements CameraModule, private FocusOverlayManager mFocusManager; private LocationManager mLocationManager; - private OrientationManager mOrientationManager; private int mPendingSwitchCameraId; private final Handler mHandler = new MainHandler(); private VideoUI mUI; @@ -276,27 +285,42 @@ public class VideoModule implements CameraModule, return mIsMute; } - protected class CameraOpenThread extends Thread { + private class OpenCameraThread extends Thread { @Override public void run() { openCamera(); - if (mFocusManager == null) initializeFocusManager(); + readVideoPreferences(); + startPreview(); } } + private OpenCameraThread mOpenCameraThread = null; + private void openCamera() { - if (mCameraDevice == null) { - mCameraDevice = CameraUtil.openCamera( - mActivity, mCameraId, mHandler, - mActivity.getCameraOpenErrorCallback()); + if (mPaused) { + return; } + + mCameraDevice = CameraUtil.openCamera( + mActivity, mCameraId, mHandler, + mActivity.getCameraOpenErrorCallback()); if (mCameraDevice == null) { // Error. return; } mParameters = mCameraDevice.getParameters(); + mCameraPreviewParamsReady = true; + mInitialParams = mParameters; + + if (mFocusManager == null) { + initializeFocusManager(); + } else { + mFocusManager.setParameters(mInitialParams); + } initializeCapabilities(); + mPreviewFocused = arePreviewControlsVisible(); + mHandler.sendEmptyMessageDelayed(CAMERA_OPEN_DONE, 100); } //QCOM data Members Starts here @@ -376,10 +400,17 @@ public class VideoModule implements CameraModule, private String mDefaultAntibanding = null; boolean mUnsupportedProfile = false; + private boolean mFirstTimeInitialized = false; + public void onScreenSizeChanged(int width, int height) { if (mFocusManager != null) mFocusManager.setPreviewSize(width, height); } + @Override + public void onPreviewRectChanged(Rect previewRect) { + if (mFocusManager != null) mFocusManager.setPreviewRect(previewRect); + } + // This Handler is used to post message back onto the main thread of the // application private class MainHandler extends Handler { @@ -397,27 +428,6 @@ public class VideoModule implements CameraModule, break; } - case UPDATE_RECORD_TIME: { - updateRecordingTime(); - break; - } - - case CHECK_DISPLAY_ROTATION: { - // Restart the preview if display rotation has changed. - // Sometimes this happens when the device is held upside - // down and camera app is opened. Rotation animation will - // take some time and the rotation value we have got may be - // wrong. Framework does not have a callback for this now. - if ((CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) - && !mMediaRecorderRecording && !mSwitchingCamera) { - startPreview(); - } - if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) { - mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100); - } - break; - } - case SHOW_TAP_TO_SNAPSHOT_TOAST: { showTapToSnapshotToast(); break; @@ -437,6 +447,16 @@ public class VideoModule implements CameraModule, break; } + case CAMERA_OPEN_DONE: { + onCameraOpened(); + break; + } + + case ON_PREVIEW_STARTED: { + onPreviewStarted(); + break; + } + case HANDLE_FLASH_TORCH_DELAY: { forceFlashOff(!mPreviewFocused); break; @@ -447,6 +467,12 @@ public class VideoModule implements CameraModule, break; } + case SET_VIDEO_UI_PARAMS: { + setCameraParameters(UPDATE_PARAM_PREFERENCE); + mUI.updateOnScreenIndicators(mParameters, mPreferences); + break; + } + default: Log.v(TAG, "Unhandled message: " + msg.what); break; @@ -470,6 +496,20 @@ public class VideoModule implements CameraModule, } } + private void initializeFirstTime() { + if (mFirstTimeInitialized || mPaused) { + return; + } + + // Initialize location service. + boolean recordLocation = RecordLocationPreference.get(mPreferences); + mLocationManager.recordLocation(recordLocation); + + mUI.initializeFirstTime(); + + mFirstTimeInitialized = true; + } + private String createName(long dateTaken) { Date date = new Date(dateTaken); SimpleDateFormat dateFormat = new SimpleDateFormat( @@ -489,12 +529,6 @@ public class VideoModule implements CameraModule, } } - private void initializeSurfaceView() { - if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) { // API level < 16 - mUI.initializeSurfaceView(); - } - } - public void reinit() { mPreferences = ComboPreferences.get(mActivity); if (mPreferences == null) { @@ -510,7 +544,7 @@ public class VideoModule implements CameraModule, @Override public void init(CameraActivity activity, View root) { mActivity = activity; - mUI = new VideoUI(activity, this, (ViewGroup) root); + mPreferences = ComboPreferences.get(mActivity); if (mPreferences == null) { mPreferences = new ComboPreferences(mActivity); @@ -518,13 +552,16 @@ public class VideoModule implements CameraModule, CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal(), activity); mCameraId = getPreferredCameraId(mPreferences); + mContentResolver = mActivity.getContentResolver(); + + // Surface texture is from camera screen nail and startPreview needs it. + // This must be done before startPreview. + mIsVideoCaptureIntent = isVideoCaptureIntent(); mPreferences.setLocalId(mActivity, mCameraId); CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); - // we need to reset exposure for the preview - resetExposureCompensation(); - mOrientationManager = new OrientationManager(mActivity); + mUI = new VideoUI(activity, this, (ViewGroup) root); // Power shutter mActivity.initPowerShutter(mPreferences); @@ -536,31 +573,14 @@ public class VideoModule implements CameraModule, * To reduce startup time, we start the preview in another thread. * We make sure the preview is started at the end of onCreate. */ - CameraOpenThread cameraOpenThread = new CameraOpenThread(); - cameraOpenThread.start(); - - mContentResolver = mActivity.getContentResolver(); + if (mOpenCameraThread == null) { + mOpenCameraThread = new OpenCameraThread(); + mOpenCameraThread.start(); + } Storage.setSaveSDCard( mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); mSaveToSDCard = Storage.isSaveSDCard(); - // Surface texture is from camera screen nail and startPreview needs it. - // This must be done before startPreview. - mIsVideoCaptureIntent = isVideoCaptureIntent(); - initializeSurfaceView(); - - // Make sure camera device is opened. - try { - cameraOpenThread.join(); - if (mCameraDevice == null) { - return; - } - } catch (InterruptedException ex) { - // ignore - } - - readVideoPreferences(); - mUI.setPrefChangedListener(this); mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false); mLocationManager = new LocationManager(mActivity, this); @@ -569,10 +589,7 @@ public class VideoModule implements CameraModule, setDisplayOrientation(); mUI.showTimeLapseUI(mCaptureTimeLapse); - initializeVideoSnapshot(); - resizeForPreviewAspectRatio(); - initializeVideoControl(); mPendingSwitchCameraId = -1; } @@ -627,15 +644,20 @@ public class VideoModule implements CameraModule, @Override public void setFocusParameters() { - if (mFocusAreaSupported) - mParameters.setFocusAreas(mFocusManager.getFocusAreas()); - if (mMeteringAreaSupported) - mParameters.setMeteringAreas(mFocusManager.getMeteringAreas()); - setAutoExposureLockIfSupported(); - setAutoWhiteBalanceLockIfSupported(); - if (mFocusAreaSupported || mMeteringAreaSupported) { - mParameters.setFocusMode(mFocusManager.getFocusMode(true)); - mCameraDevice.setParameters(mParameters); + if (mCameraDevice == null) { + return; + } + synchronized (mCameraDevice) { + if (mFocusAreaSupported) + mParameters.setFocusAreas(mFocusManager.getFocusAreas()); + if (mMeteringAreaSupported) + mParameters.setMeteringAreas(mFocusManager.getMeteringAreas()); + setAutoExposureLockIfSupported(); + setAutoWhiteBalanceLockIfSupported(); + if (mFocusAreaSupported || mMeteringAreaSupported) { + mParameters.setFocusMode(mFocusManager.getFocusMode(true)); + mCameraDevice.setParameters(mParameters); + } } } @@ -740,11 +762,6 @@ public class VideoModule implements CameraModule, } - private void initializeVideoControl() { - loadCameraPreferences(); - mUI.initializePopup(mPreferenceGroup); - } - @Override public void onOrientationChanged(int orientation) { // We keep the last known orientation. So if the user first orient @@ -758,9 +775,11 @@ public class VideoModule implements CameraModule, Log.v(TAG, "onOrientationChanged, update parameters"); if ((mCameraDevice != null) && (mParameters != null) && (true == mPreviewing) && !mMediaRecorderRecording){ - setFlipValue(); - updatePowerMode(); - mCameraDevice.setParameters(mParameters); + synchronized (mCameraDevice) { + setFlipValue(); + updatePowerMode(); + mCameraDevice.setParameters(mParameters); + } } mUI.tryToCloseSubList(); mUI.setOrientation(newOrientation, true); @@ -1167,7 +1186,6 @@ public class VideoModule implements CameraModule, mDesiredPreviewWidth = optimalSize.width; mDesiredPreviewHeight = optimalSize.height; } - mUI.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight); Log.v(TAG, "mDesiredPreviewWidth=" + mDesiredPreviewWidth + ". mDesiredPreviewHeight=" + mDesiredPreviewHeight); } @@ -1227,51 +1245,35 @@ public class VideoModule implements CameraModule, @Override public void onResumeAfterSuper() { - mUI.enableShutter(false); + if (mOpenCameraThread == null) { + mOpenCameraThread = new OpenCameraThread(); + mOpenCameraThread.start(); + } + + mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.SURFACE_VIEW); + + if (!mFirstTimeInitialized) { + initializeFirstTime(); + } + mZoomValue = 0; - mUI.showSurfaceView(); + AudioManager am = (AudioManager)mActivity.getSystemService(Context.AUDIO_SERVICE); mWasMute = am.isMicrophoneMute(); if(mWasMute != mIsMute) { setMute(mIsMute, false); } - initializeVideoControl(); - resetExposureCompensation(); showVideoSnapshotUI(false); installIntentFilter(); - if (!mPreviewing) { - openCamera(); - if (mCameraDevice == null) { - return; - } - readVideoPreferences(); - resizeForPreviewAspectRatio(); - startPreview(); - } else { - // preview already started - mUI.enableShutter(true); - } - - mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.SURFACE_VIEW); - mUI.initDisplayChangeListener(); - // Initializing it here after the preview is started. - mUI.initializeZoom(mParameters); - mUI.setPreviewGesturesVideoUI(); - mUI.setSwitcherIndex(); keepScreenOnAwhile(); + mUI.updateOnScreenIndicators(mParameters, mPreferences); + mUI.setSwitcherIndex(); - mOrientationManager.resume(); - // Initialize location service. - boolean recordLocation = RecordLocationPreference.get(mPreferences); - mLocationManager.recordLocation(recordLocation); - - if (mPreviewing) { - mOnResumeTime = SystemClock.uptimeMillis(); - mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100); - } + // From onResume + mUI.setOrientationIndicator(0, false); UsageStatistics.onContentViewChanged( UsageStatistics.COMPONENT_CAMERA, "VideoModule"); @@ -1312,82 +1314,93 @@ public class VideoModule implements CameraModule, if (mParameters == null || mCameraDevice == null) return index; // Set zoom parameters asynchronously mParameters.setZoom(mZoomValue); - mCameraDevice.setParameters(mParameters); + updateCameraParametersZoom(); Parameters p = mCameraDevice.getParameters(); if (p != null) return p.getZoom(); return index; } private void startPreview() { - Log.v(TAG, "startPreview"); + Log.e(TAG, String.format("startPreview paused=%b device=%b params=%b", mPaused, mCameraDevice == null, mParameters == null), new Throwable()); + if (mPaused || mCameraDevice == null || mParameters == null) { + return; + } + mStartPrevPending = true; - SurfaceHolder sh = null; - Log.v(TAG, "startPreview: SurfaceHolder (MDP path)"); - sh = mUI.getSurfaceHolder(); + synchronized (mCameraDevice) { + SurfaceHolder sh = null; + Log.v(TAG, "startPreview: SurfaceHolder (MDP path)"); + if (mUI != null) { + sh = mUI.getSurfaceHolder(); + } + + // Let UI set its expected aspect ratio + mCameraDevice.setPreviewDisplay(sh); + } - if (!mPreferenceRead || mPaused == true || mCameraDevice == null) { - mStartPrevPending = false; + if (!mCameraPreviewParamsReady) { + Log.w(TAG, "startPreview: parameters for preview are not ready."); return; } mErrorCallback.setActivity(mActivity); mCameraDevice.setErrorCallback(mErrorCallback); - if (mPreviewing == true) { + + if (mRestartPreview && mPreviewing) { stopPreview(); + mRestartPreview = false; } - setDisplayOrientation(); - mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation); - setCameraParameters(true); + if (mFocusManager == null) initializeFocusManager(); - try { - mCameraDevice.setPreviewDisplay(sh); - mCameraDevice.setOneShotPreviewCallback(mHandler, - new CameraManager.CameraPreviewDataCallback() { - @Override - public void onPreviewFrame(byte[] data, CameraProxy camera) { - mUI.hidePreviewCover(); - } - }); - mCameraDevice.startPreview(); - mPreviewing = true; - mCameraDevice.setOneShotPreviewCallback(mHandler, + updateTorchDelayed(); + + setCameraParameters(UPDATE_PARAM_ALL); + + mCameraDevice.setOneShotPreviewCallback(mHandler, new CameraManager.CameraPreviewDataCallback() { @Override public void onPreviewFrame(byte[] data, CameraProxy camera) { mUI.hidePreviewCover(); } }); - onPreviewStarted(); - } catch (Throwable ex) { - closeCamera(); - throw new RuntimeException("startPreview failed", ex); - } - mStartPrevPending = false; + mCameraDevice.startPreview(); - mFocusManager.onPreviewStarted(); + mHandler.sendEmptyMessage(ON_PREVIEW_STARTED); + + setDisplayOrientation(); + mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation); + + mStartPrevPending = false; } private void onPreviewStarted() { - mUI.enableShutter(true); + mPreviewing = true; + mFocusManager.onPreviewStarted(); startFaceDetection(); + mUI.enableShutter(true); } @Override public void stopPreview() { - mStopPrevPending = true; + if (mCameraDevice == null) { + return; + } + synchronized (mCameraDevice) { + mStopPrevPending = true; - if (mFocusManager != null) mFocusManager.onPreviewStopped(); + if (mFocusManager != null) mFocusManager.onPreviewStopped(); - if (!mPreviewing) { + if (!mPreviewing) { + mStopPrevPending = false; + return; + } + mCameraDevice.stopPreview(); + mPreviewing = false; mStopPrevPending = false; - return; + mUI.enableShutter(false); + stopFaceDetection(); } - mCameraDevice.stopPreview(); - mPreviewing = false; - mStopPrevPending = false; - mUI.enableShutter(false); - stopFaceDetection(); } private void closeCamera() { @@ -1396,13 +1409,15 @@ public class VideoModule implements CameraModule, Log.d(TAG, "already stopped."); return; } - mCameraDevice.setZoomChangeListener(null); - mCameraDevice.setErrorCallback(null); - mCameraDevice.setFaceDetectionCallback(null, null); - if (mActivity.isForceReleaseCamera()) { - CameraHolder.instance().strongRelease(); - } else { - CameraHolder.instance().release(); + synchronized (mCameraDevice) { + mCameraDevice.setZoomChangeListener(null); + mCameraDevice.setErrorCallback(null); + mCameraDevice.setFaceDetectionCallback(null, null); + if (mActivity.isForceReleaseCamera()) { + CameraHolder.instance().strongRelease(); + } else { + CameraHolder.instance().release(); + } } mCameraDevice = null; mPreviewing = false; @@ -1412,6 +1427,17 @@ public class VideoModule implements CameraModule, mFaceDetectionStarted = false; } + private void onCameraOpened() { + Log.v(TAG, "onCameraOpened"); + openCameraCommon(); + initializeVideoSnapshot(); + + resizeForPreviewAspectRatio(); + mFocusManager.setFocusRing(mUI.getFocusRing()); + mUI.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight); + resetExposureCompensation(); + } + private void releasePreviewResources() { if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) { mUI.hideSurfaceView(); @@ -1444,9 +1470,7 @@ public class VideoModule implements CameraModule, resetScreenOn(); if (mLocationManager != null) mLocationManager.recordLocation(false); - mOrientationManager.pause(); - mHandler.removeMessages(CHECK_DISPLAY_ROTATION); mHandler.removeMessages(SWITCH_CAMERA); mHandler.removeMessages(SWITCH_CAMERA_START_ANIMATION); mHandler.removeMessages(HANDLE_FLASH_TORCH_DELAY); @@ -1465,7 +1489,28 @@ public class VideoModule implements CameraModule, @Override public void onPauseAfterSuper() { - if (mFocusManager != null) mFocusManager.removeMessages(); + Log.v(TAG, "On pause."); + + mUI.showPreviewCover(); + mUI.hideSurfaceView(); + + // Reset the focus first. Camera CTS does not guarantee that + // cancelAutoFocus is allowed after preview stops. + if (mCameraDevice != null) { + mCameraDevice.cancelAutoFocus(); + } + + try { + if (mOpenCameraThread != null) { + mOpenCameraThread.join(); + } + } catch (InterruptedException ex) { + // ignore + } + mOpenCameraThread = null; + + stopPreview(); + closeCamera(); } /** @@ -1483,9 +1528,13 @@ public class VideoModule implements CameraModule, boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT); String[] defaultFocusModes = mActivity.getResources().getStringArray( R.array.pref_video_focusmode_default_array); - mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes, - mParameters, this, mirror, - mActivity.getMainLooper(), mUI.getFocusRing()); + synchronized (this) { + if (mFocusManager == null) { + mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes, + mInitialParams, this, mirror, + mActivity.getMainLooper(), mUI.getFocusRing()); + } + } } } @@ -1857,6 +1906,8 @@ public class VideoModule implements CameraModule, private void releaseMediaRecorder() { Log.v(TAG, "Releasing media recorder."); + mUI.stopRecordingTimer(); + if (mMediaRecorder != null) { cleanupEmptyFile(); mMediaRecorder.reset(); @@ -2099,12 +2150,12 @@ public class VideoModule implements CameraModule, mMediaRecorderRecording = true; mMediaRecorderPausing = false; - mUI.resetPauseButton(); - mRecordingTotalTime = 0L; - mRecordingStartTime = SystemClock.uptimeMillis(); + mUI.showRecordingUI(true); - updateRecordingTime(); + mUI.startRecordingTimer(mProfile.videoFrameRate, + mTimeBetweenTimeLapseFrameCaptureMs, mMaxVideoDurationInMs); + keepScreenOn(); UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_CAPTURE_START, "Video"); @@ -2149,18 +2200,17 @@ public class VideoModule implements CameraModule, mUI.showTimeLapseUI(false); } - private void pauseVideoRecording() { + @Override + public void onButtonPause() { Log.v(TAG, "pauseVideoRecording"); mMediaRecorderPausing = true; - mRecordingTotalTime += SystemClock.uptimeMillis() - mRecordingStartTime; mMediaRecorder.pause(); } - private void resumeVideoRecording() { + @Override + public void onButtonContinue() { Log.v(TAG, "resumeVideoRecording"); mMediaRecorderPausing = false; - mRecordingStartTime = SystemClock.uptimeMillis(); - updateRecordingTime(); if (!HAS_RESUME_SUPPORTED){ mMediaRecorder.start(); } else { @@ -2247,7 +2297,9 @@ public class VideoModule implements CameraModule, } else { mParameters.setPreviewFrameRate(30); } - mCameraDevice.setParameters(mParameters); + synchronized (mCameraDevice) { + mCameraDevice.setParameters(mParameters); + } } } } @@ -2257,8 +2309,7 @@ public class VideoModule implements CameraModule, UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, fail ? UsageStatistics.ACTION_CAPTURE_FAIL : UsageStatistics.ACTION_CAPTURE_DONE, "Video", - mMediaRecorderPausing ? mRecordingTotalTime : - SystemClock.uptimeMillis() - mRecordingStartTime + mRecordingTotalTime); + mUI.getRecordingTime()); mStopRecPending = false; return fail; } @@ -2279,111 +2330,6 @@ public class VideoModule implements CameraModule, mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } - private static String millisecondToTimeString(long milliSeconds, boolean displayCentiSeconds) { - long seconds = milliSeconds / 1000; // round down to compute seconds - long minutes = seconds / 60; - long hours = minutes / 60; - long remainderMinutes = minutes - (hours * 60); - long remainderSeconds = seconds - (minutes * 60); - - StringBuilder timeStringBuilder = new StringBuilder(); - - // Hours - if (hours > 0) { - if (hours < 10) { - timeStringBuilder.append('0'); - } - timeStringBuilder.append(hours); - - timeStringBuilder.append(':'); - } - - // Minutes - if (remainderMinutes < 10) { - timeStringBuilder.append('0'); - } - timeStringBuilder.append(remainderMinutes); - timeStringBuilder.append(':'); - - // Seconds - if (remainderSeconds < 10) { - timeStringBuilder.append('0'); - } - timeStringBuilder.append(remainderSeconds); - - // Centi seconds - if (displayCentiSeconds) { - timeStringBuilder.append('.'); - long remainderCentiSeconds = (milliSeconds - seconds * 1000) / 10; - if (remainderCentiSeconds < 10) { - timeStringBuilder.append('0'); - } - timeStringBuilder.append(remainderCentiSeconds); - } - - return timeStringBuilder.toString(); - } - - private long getTimeLapseVideoLength(long deltaMs) { - // For better approximation calculate fractional number of frames captured. - // This will update the video time at a higher resolution. - double numberOfFrames = (double) deltaMs / mTimeBetweenTimeLapseFrameCaptureMs; - return (long) (numberOfFrames / mProfile.videoFrameRate * 1000); - } - - private void updateRecordingTime() { - if (!mMediaRecorderRecording) { - return; - } - if (mMediaRecorderPausing) { - return; - } - - long now = SystemClock.uptimeMillis(); - long delta = now - mRecordingStartTime + mRecordingTotalTime; - - // Starting a minute before reaching the max duration - // limit, we'll countdown the remaining time instead. - boolean countdownRemainingTime = (mMaxVideoDurationInMs != 0 - && delta >= mMaxVideoDurationInMs - 60000); - - long deltaAdjusted = delta; - if (countdownRemainingTime) { - deltaAdjusted = Math.max(0, mMaxVideoDurationInMs - deltaAdjusted) + 999; - } - String text; - - long targetNextUpdateDelay; - if (!mCaptureTimeLapse) { - text = millisecondToTimeString(deltaAdjusted, false); - targetNextUpdateDelay = 1000; - } else { - // The length of time lapse video is different from the length - // of the actual wall clock time elapsed. Display the video length - // only in format hh:mm:ss.dd, where dd are the centi seconds. - text = millisecondToTimeString(getTimeLapseVideoLength(delta), true); - targetNextUpdateDelay = mTimeBetweenTimeLapseFrameCaptureMs; - } - - mUI.setRecordingTime(text); - - if (mRecordingTimeCountsDown != countdownRemainingTime) { - // Avoid setting the color on every update, do it only - // when it needs changing. - mRecordingTimeCountsDown = countdownRemainingTime; - - int color = mActivity.getResources().getColor(countdownRemainingTime - ? R.color.recording_time_remaining_text - : R.color.recording_time_elapsed_text); - - mUI.setRecordingTimeTextColor(color); - } - - long actualNextUpdateDelay = targetNextUpdateDelay - (delta % targetNextUpdateDelay); - mHandler.sendEmptyMessageDelayed( - UPDATE_RECORD_TIME, actualNextUpdateDelay); - } - private static boolean isSupported(String value, List supported) { return supported == null ? false : supported.indexOf(value) >= 0; } @@ -2439,7 +2385,16 @@ public class VideoModule implements CameraModule, } } - private void qcomSetCameraParameters(){ + public void overrideSettings(final String... keyvalues) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.overrideSettings(keyvalues); + } + }); + } + + private void qcomSetCameraParameters() { // add QCOM Parameters here // Set color effect parameter. Log.i(TAG,"NOTE: qcomSetCameraParameters " + videoWidth + " x " + videoHeight); @@ -2462,8 +2417,10 @@ public class VideoModule implements CameraModule, CameraSettings.getSupportedDISModes(mParameters))) { mParameters.set(CameraSettings.KEY_QC_DIS_MODE, mActivity.getString(R.string.pref_camera_dis_value_disable)); - mUI.overrideSettings(CameraSettings.KEY_DIS, + + overrideSettings(CameraSettings.KEY_DIS, mActivity.getString(R.string.pref_camera_dis_value_disable)); + mIsDISEnabled = false; } else { Log.e(TAG, "Not supported IS mode = " + @@ -2607,8 +2564,9 @@ public class VideoModule implements CameraModule, pref_camera_video_tnr_value_off))) { mParameters.set(CameraSettings.KEY_QC_VIDEO_CDS_MODE, mActivity.getString(R.string.pref_camera_video_cds_value_off)); - mUI.overrideSettings(CameraSettings.KEY_VIDEO_CDS_MODE, + overrideSettings(CameraSettings.KEY_VIDEO_CDS_MODE, mActivity.getString(R.string.pref_camera_video_cds_value_off)); + if (!mIsVideoCDSUpdated) { if (video_cds != null) { mPrevSavedVideoCDS = mTempVideoCDS; @@ -2618,7 +2576,7 @@ public class VideoModule implements CameraModule, } } else if (mIsVideoTNREnabled) { mParameters.set(CameraSettings.KEY_QC_VIDEO_CDS_MODE, mPrevSavedVideoCDS); - mUI.overrideSettings(CameraSettings.KEY_VIDEO_CDS_MODE, mPrevSavedVideoCDS); + overrideSettings(CameraSettings.KEY_VIDEO_CDS_MODE, mPrevSavedVideoCDS); mIsVideoTNREnabled = false; mIsVideoCDSUpdated = false; mOverrideCDS = true; @@ -2626,7 +2584,7 @@ public class VideoModule implements CameraModule, mTempVideoCDS = video_cds; } mParameters.set(CameraSettings.KEY_QC_VIDEO_TNR_MODE, video_tnr); - mUI.overrideSettings(CameraSettings.KEY_VIDEO_TNR_MODE, video_tnr); + overrideSettings(CameraSettings.KEY_VIDEO_TNR_MODE, video_tnr); } String noiseReductionMode = mPreferences.getString( @@ -2643,7 +2601,7 @@ public class VideoModule implements CameraModule, pref_camera_video_cds_value_on))) { mParameters.set(CameraSettings.KEY_QC_VIDEO_CDS_MODE, mActivity.getString(R.string.pref_camera_video_cds_value_off)); - mUI.overrideSettings(CameraSettings.KEY_VIDEO_CDS_MODE, + overrideSettings(CameraSettings.KEY_VIDEO_CDS_MODE, mActivity.getString(R.string.pref_camera_video_cds_value_off)); Toast.makeText(mActivity, R.string.disable_CDS_during_HighQualityNoiseReduction, Toast.LENGTH_LONG).show(); @@ -2656,7 +2614,7 @@ public class VideoModule implements CameraModule, pref_camera_video_tnr_value_on))) { mParameters.set(CameraSettings.KEY_QC_VIDEO_TNR_MODE, mActivity.getString(R.string.pref_camera_video_tnr_value_off)); - mUI.overrideSettings(CameraSettings.KEY_VIDEO_TNR_MODE, + overrideSettings(CameraSettings.KEY_VIDEO_TNR_MODE, mActivity.getString(R.string.pref_camera_video_tnr_value_off)); Toast.makeText(mActivity, R.string.disable_TNR_during_HighQualityNoiseReduction, Toast.LENGTH_LONG).show(); @@ -2677,26 +2635,26 @@ public class VideoModule implements CameraModule, if (seeMoreMode.equals( mActivity.getString(R.string.pref_camera_see_more_value_on)) && video_cds.equals(mActivity.getString(R.string. - pref_camera_video_cds_value_on))) { + pref_camera_video_cds_value_on))) { mParameters.set(CameraSettings.KEY_QC_VIDEO_CDS_MODE, - mActivity.getString(R.string.pref_camera_video_cds_value_off)); - mUI.overrideSettings(CameraSettings.KEY_VIDEO_CDS_MODE, - mActivity.getString(R.string.pref_camera_video_cds_value_off)); + mActivity.getString(R.string.pref_camera_video_cds_value_off)); + overrideSettings(CameraSettings.KEY_VIDEO_CDS_MODE, + mActivity.getString(R.string.pref_camera_video_cds_value_off)); Toast.makeText(mActivity, R.string.disable_CDS_during_SeeMore, - Toast.LENGTH_LONG).show(); + Toast.LENGTH_LONG).show(); } /* Disable TNR */ if (seeMoreMode.equals( mActivity.getString(R.string.pref_camera_see_more_value_on)) && video_tnr.equals(mActivity.getString(R.string. - pref_camera_video_tnr_value_on))) { + pref_camera_video_tnr_value_on))) { mParameters.set(CameraSettings.KEY_QC_VIDEO_TNR_MODE, - mActivity.getString(R.string.pref_camera_video_tnr_value_off)); - mUI.overrideSettings(CameraSettings.KEY_VIDEO_TNR_MODE, - mActivity.getString(R.string.pref_camera_video_tnr_value_off)); + mActivity.getString(R.string.pref_camera_video_tnr_value_off)); + overrideSettings(CameraSettings.KEY_VIDEO_TNR_MODE, + mActivity.getString(R.string.pref_camera_video_tnr_value_off)); Toast.makeText(mActivity, R.string.disable_TNR_during_SeeMore, - Toast.LENGTH_LONG).show(); + Toast.LENGTH_LONG).show(); } /* Disable NR */ @@ -2706,7 +2664,7 @@ public class VideoModule implements CameraModule, pref_camera_noise_reduction_value_off))) { mParameters.set(CameraSettings.KEY_QC_NOISE_REDUCTION_MODE, mActivity.getString(R.string.pref_camera_noise_reduction_value_off)); - mUI.overrideSettings(CameraSettings.KEY_NOISE_REDUCTION, + overrideSettings(CameraSettings.KEY_NOISE_REDUCTION, mActivity.getString(R.string.pref_camera_noise_reduction_value_off)); Toast.makeText(mActivity, R.string.disable_NR_during_SeeMore, Toast.LENGTH_LONG).show(); @@ -2746,9 +2704,9 @@ public class VideoModule implements CameraModule, (disMode.equals("enable") && (rate > PERSIST_EIS_MAX_FPS)) || ((hdr != null) && (!hdr.equals("off"))) ) { Log.v(TAG,"HDR/DIS/Time Lapse ON for HFR/HSR selection, turning HFR/HSR off"); - mParameters.setVideoHighFrameRate("off"); - mParameters.set(CameraSettings.KEY_VIDEO_HSR, "off"); - mUI.overrideSettings(CameraSettings.KEY_VIDEO_HIGH_FRAME_RATE, "off"); + mParameters.setVideoHighFrameRate("off"); + mParameters.set(CameraSettings.KEY_VIDEO_HSR, "off"); + overrideSettings(CameraSettings.KEY_VIDEO_HIGH_FRAME_RATE, "off"); } } @@ -2757,15 +2715,15 @@ public class VideoModule implements CameraModule, if (biggestSize.width <= videoWidth || biggestSize.height <= videoHeight) { if (disMode.equals("enable")) { - Log.v(TAG,"DIS is not supported for this video quality"); + Log.v(TAG, "DIS is not supported for this video quality"); RotateTextToast.makeText(mActivity, R.string.error_app_unsupported_dis, - Toast.LENGTH_LONG).show(); + Toast.LENGTH_LONG).show(); mParameters.set(CameraSettings.KEY_QC_DIS_MODE, "disable"); - mUI.overrideSettings(CameraSettings.KEY_DIS,"disable"); + overrideSettings(CameraSettings.KEY_DIS, "disable"); mIsDISEnabled = false; } } - //setting video rotation + //setting video rotation String videoRotation = mPreferences.getString( CameraSettings.KEY_VIDEO_ROTATION, mActivity.getString(R.string.pref_camera_video_rotation_default)); @@ -2802,11 +2760,27 @@ public class VideoModule implements CameraModule, } } - @SuppressWarnings("deprecation") - private void setCameraParameters(boolean isFlashDelay) { - Log.d(TAG,"Preview dimension in App->"+mDesiredPreviewWidth+"X"+mDesiredPreviewHeight); + private void updateCameraParametersZoom() { + if (mCameraDevice == null) { + return; + } + synchronized (mCameraDevice) { + // Set zoom. + if (mParameters.isZoomSupported()) { + Parameters p = mCameraDevice.getParameters(); + mZoomValue = p.getZoom(); + mParameters.setZoom(mZoomValue); + } + } + } + + private void updateTorchDelayed() { + mHandler.sendEmptyMessageDelayed(HANDLE_FLASH_TORCH_DELAY, 800); + } + + private void updateCameraParametersInitialize() { mParameters.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight); - mParameters.set("video-size", mProfile.videoFrameWidth+"x"+mProfile.videoFrameHeight); + mParameters.set("video-size", mProfile.videoFrameWidth + "x" + mProfile.videoFrameHeight); int[] fpsRange = CameraUtil.getMaxPreviewFpsRange(mParameters); if (fpsRange.length > 0) { mParameters.setPreviewFpsRange( @@ -2816,49 +2790,17 @@ public class VideoModule implements CameraModule, mParameters.setPreviewFrameRate(mProfile.videoFrameRate); } - if (isFlashDelay) { - mHandler.sendEmptyMessageDelayed(HANDLE_FLASH_TORCH_DELAY, 800); - } else { - forceFlashOffIfSupported(!mPreviewFocused); - } + forceFlashOffIfSupported(!mPreviewFocused); + videoWidth = mProfile.videoFrameWidth; videoHeight = mProfile.videoFrameHeight; - Log.i(TAG,"NOTE: SetCameraParameters " + videoWidth + " x " + videoHeight); + Log.i(TAG, "NOTE: SetCameraParameters " + videoWidth + " x " + videoHeight); String recordSize = videoWidth + "x" + videoHeight; - Log.e(TAG,"Video dimension in App->"+recordSize); + Log.e(TAG, "Video dimension in App->" + recordSize); if (CameraUtil.isSupported(mParameters, "video-size")) { mParameters.set("video-size", recordSize); } - // Set exposure compensation - int value = CameraSettings.readExposure(mPreferences); - int max = mParameters.getMaxExposureCompensation(); - int min = mParameters.getMinExposureCompensation(); - if (value >= min && value <= max) { - mParameters.setExposureCompensation(value); - } else { - Log.w(TAG, "invalid exposure range: " + value); - } - // Set white balance parameter. - String whiteBalance = mPreferences.getString( - CameraSettings.KEY_WHITE_BALANCE, - mActivity.getString(R.string.pref_camera_whitebalance_default)); - 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()) { - Parameters p = mCameraDevice.getParameters(); - mZoomValue = p.getZoom(); - mParameters.setZoom(mZoomValue); - } // Set focus mode mParameters.setFocusMode(mFocusManager.getFocusMode(true)); @@ -2871,73 +2813,112 @@ public class VideoModule implements CameraModule, if ("true".equals(vstabSupported)) { mParameters.set("video-stabilization", "true"); } + } + + // We separate the parameters into several subsets, so we can update only + // the subsets actually need updating. The PREFERENCE set needs extra + // locking because the preference can be changed from GLThread as well. + private void setCameraParameters(int updateSet) { + if (mCameraDevice == null) { + return; + } + synchronized (mCameraDevice) { + if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) { + updateCameraParametersInitialize(); + } - // Set picture size. - // The logic here is different from the logic in still-mode camera. - // There we determine the preview size based on the picture size, but - // here we determine the picture size based on the preview size. - String videoSnapshotSize = mPreferences.getString( - CameraSettings.KEY_VIDEO_SNAPSHOT_SIZE, - mActivity.getString(R.string.pref_camera_videosnapsize_default)); - Size optimalSize; - if(videoSnapshotSize.equals("auto")) { - List supported = mParameters.getSupportedPictureSizes(); - optimalSize = CameraUtil.getOptimalVideoSnapshotPictureSize(supported, - (double) mDesiredPreviewWidth / mDesiredPreviewHeight); - Size original = mParameters.getPictureSize(); - if (!original.equals(optimalSize)) { - mParameters.setPictureSize(optimalSize.width, optimalSize.height); + if ((updateSet & UPDATE_PARAM_ZOOM) != 0) { + updateCameraParametersZoom(); } - } else { - CameraSettings.setCameraPictureSize( - videoSnapshotSize, - mParameters.getSupportedPictureSizes(), - mParameters); - optimalSize = mParameters.getPictureSize(); - } - Log.v(TAG, "Video snapshot size is " + optimalSize.width + "x" + - optimalSize.height); + if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) { + updateCameraParametersPreference(); + } - // Set jpegthumbnail size - // Set a jpegthumbnail size that is closest to the Picture height and has - // the right aspect ratio. - Size size = mParameters.getPictureSize(); - List sizes = mParameters.getSupportedJpegThumbnailSizes(); - optimalSize = CameraUtil.getOptimalJpegThumbnailSize(sizes, - (double) size.width / size.height); - Size original = mParameters.getJpegThumbnailSize(); - if (!original.equals(optimalSize)) { - mParameters.setJpegThumbnailSize(optimalSize.width, optimalSize.height); + CameraUtil.dumpParameters(mParameters); + mCameraDevice.setParameters(mParameters); + mFocusManager.setParameters(mParameters); } - Log.v(TAG, "Thumbnail size is " + optimalSize.width + "x" + optimalSize.height); - - // Set JPEG quality. - int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, - CameraProfile.QUALITY_HIGH); - mParameters.setJpegQuality(jpegQuality); + } - CameraUtil.dumpParameters(mParameters); + private void updateCameraParametersPreference() { + if (mCameraDevice == null) { + return; + } + synchronized (mCameraDevice) { + Log.d(TAG, "Preview dimension in App->" + mDesiredPreviewWidth + "X" + mDesiredPreviewHeight); - //Call Qcom related Camera Parameters - qcomSetCameraParameters(); + // Set exposure compensation + int value = CameraSettings.readExposure(mPreferences); + int max = mParameters.getMaxExposureCompensation(); + int min = mParameters.getMinExposureCompensation(); + if (value >= min && value <= max) { + mParameters.setExposureCompensation(value); + } else { + Log.w(TAG, "invalid exposure range: " + value); + } + // Set white balance parameter. + String whiteBalance = mPreferences.getString( + CameraSettings.KEY_WHITE_BALANCE, + mActivity.getString(R.string.pref_camera_whitebalance_default)); + if (isSupported(whiteBalance, + mParameters.getSupportedWhiteBalance())) { + mParameters.setWhiteBalance(whiteBalance); + } else { + whiteBalance = mParameters.getWhiteBalance(); + if (whiteBalance == null) { + whiteBalance = Parameters.WHITE_BALANCE_AUTO; + } + } - boolean flag = false; - if (mPreviewing) { - stopPreview(); - flag = true; - } - mCameraDevice.setParameters(mParameters); - if (flag) { - startPreview(); - } - // Keep preview size up to date. - mParameters = mCameraDevice.getParameters(); + // Set picture size. + // The logic here is different from the logic in still-mode camera. + // There we determine the preview size based on the picture size, but + // here we determine the picture size based on the preview size. + String videoSnapshotSize = mPreferences.getString( + CameraSettings.KEY_VIDEO_SNAPSHOT_SIZE, + mActivity.getString(R.string.pref_camera_videosnapsize_default)); + Size optimalSize; + if (videoSnapshotSize.equals("auto")) { + List supported = mParameters.getSupportedPictureSizes(); + optimalSize = CameraUtil.getOptimalVideoSnapshotPictureSize(supported, + (double) mDesiredPreviewWidth / mDesiredPreviewHeight); + Size original = mParameters.getPictureSize(); + if (!original.equals(optimalSize)) { + mParameters.setPictureSize(optimalSize.width, optimalSize.height); + } + } else { + CameraSettings.setCameraPictureSize( + videoSnapshotSize, + mParameters.getSupportedPictureSizes(), + mParameters); + optimalSize = mParameters.getPictureSize(); + } + + Log.v(TAG, "Video snapshot size is " + optimalSize.width + "x" + + optimalSize.height); + + // Set jpegthumbnail size + // Set a jpegthumbnail size that is closest to the Picture height and has + // the right aspect ratio. + Size size = mParameters.getPictureSize(); + List sizes = mParameters.getSupportedJpegThumbnailSizes(); + optimalSize = CameraUtil.getOptimalJpegThumbnailSize(sizes, + (double) size.width / size.height); + Size original = mParameters.getJpegThumbnailSize(); + if (!original.equals(optimalSize)) { + mParameters.setJpegThumbnailSize(optimalSize.width, optimalSize.height); + } + Log.v(TAG, "Thumbnail size is " + optimalSize.width + "x" + optimalSize.height); - // Update UI based on the new parameters. - mUI.updateOnScreenIndicators(mParameters, mPreferences); + // Set JPEG quality. + int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, + CameraProfile.QUALITY_HIGH); + mParameters.setJpegQuality(jpegQuality); - mFocusManager.setPreviewSize(videoWidth, videoHeight); + //Call Qcom related Camera Parameters + qcomSetCameraParameters(); + } } @Override @@ -2989,10 +2970,11 @@ public class VideoModule implements CameraModule, if (mPaused) { return; } - synchronized (mPreferences) { - // If mCameraDevice is not ready then we can set the parameter in - // startPreview(). - if (mCameraDevice == null) return; + // If mCameraDevice is not ready then we can set the parameter in + // startPreview(). + if (mCameraDevice == null) return; + + synchronized (mCameraDevice) { boolean recordLocation = RecordLocationPreference.get(mPreferences); mLocationManager.recordLocation(recordLocation); @@ -3008,10 +2990,9 @@ public class VideoModule implements CameraModule, resizeForPreviewAspectRatio(); startPreview(); // Parameters will be set in startPreview(). } else { - setCameraParameters(false); + mHandler.sendEmptyMessage(SET_VIDEO_UI_PARAMS); } mRestartPreview = false; - mUI.updateOnScreenIndicators(mParameters, mPreferences); Storage.setSaveSDCard( mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); mActivity.updateStorageSpaceAndHint(); @@ -3025,6 +3006,13 @@ public class VideoModule implements CameraModule, pref.setValue("" + cameraId); } + // either open a new camera or switch cameras + private void openCameraCommon() { + loadCameraPreferences(); + + mUI.onCameraOpened(mPreferenceGroup, mPreferences, mParameters, this); + } + private void switchCamera() { if (mPaused) { return; @@ -3036,48 +3024,64 @@ public class VideoModule implements CameraModule, mPendingSwitchCameraId = -1; setCameraId(mCameraId); + try { + if (mOpenCameraThread != null) { + mOpenCameraThread.join(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + mOpenCameraThread = null; + + // from onPause + try { + if (mOpenCameraThread != null) { + mOpenCameraThread.join(); + mOpenCameraThread = null; + } + } catch (InterruptedException ex) { + // ignore + } closeCamera(); mUI.collapseCameraControls(); if (mFocusManager != null) mFocusManager.removeMessages(); + // Restart the camera and initialize the UI. From onCreate. mPreferences.setLocalId(mActivity, mCameraId); CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); - openCamera(); + mCameraDevice = CameraUtil.openCamera( + mActivity, mCameraId, mHandler, + mActivity.getCameraOpenErrorCallback()); + if (mCameraDevice == null) { + Log.e(TAG, "Failed to open camera:" + mCameraId + ", aborting."); + return; + } + mParameters = mCameraDevice.getParameters(); + mInitialParams = mParameters; + initializeCapabilities(); CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT); - mParameters = mCameraDevice.getParameters(); mFocusManager.setMirror(mirror); - mFocusManager.setParameters(mParameters); - - readVideoPreferences(); - mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.SURFACE_VIEW); + setCameraParameters(UPDATE_PARAM_ALL); startPreview(); - initializeVideoSnapshot(); - resizeForPreviewAspectRatio(); - initializeVideoControl(); - - initializeCapabilities(); - // From onResume + mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.SURFACE_VIEW); mZoomValue = 0; - mUI.initializeZoom(mParameters); - mUI.setOrientationIndicator(0, false); - - // Start switch camera animation. Post a message because - // onFrameAvailable from the old camera may already exist. - mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION); - mUI.updateOnScreenIndicators(mParameters, mPreferences); + resizeForPreviewAspectRatio(); + openCameraCommon(); //Display timelapse msg depending upon selection in front/back camera. mUI.showTimeLapseUI(mCaptureTimeLapse); + + mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION); } private void initializeCapabilities() { - mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mParameters); - mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mParameters); - mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mParameters); - mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mParameters); + mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams); + mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams); + mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams); + mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams); } // Preview texture has been copied. Now camera can be released and the @@ -3144,11 +3148,13 @@ public class VideoModule implements CameraModule, * @param forceOff whether we want to force the flash off. */ private void forceFlashOff(boolean forceOff) { - if (!mPreviewing || mParameters.getFlashMode() == null) { + if (mCameraDevice == null || !mPreviewing || mParameters.getFlashMode() == null) { return; } - forceFlashOffIfSupported(forceOff); - mCameraDevice.setParameters(mParameters); + synchronized (mCameraDevice) { + forceFlashOffIfSupported(forceOff); + mCameraDevice.setParameters(mParameters); + } mUI.updateOnScreenIndicators(mParameters, mPreferences); } @@ -3237,14 +3243,12 @@ public class VideoModule implements CameraModule, if (mPaused || mPendingSwitchCameraId != -1) return; mPendingSwitchCameraId = cameraId; - Log.d(TAG, "Start to copy texture."); + + Log.v(TAG, "Start to switch camera. cameraId=" + cameraId); // We need to keep a preview frame for the animation before // releasing the camera. This will trigger onPreviewTextureCopied. - // TODO: ((CameraScreenNail) mActivity.mCameraScreenNail).copyTexture(); - // Disable all camera controls. - mSwitchingCamera = true; + //TODO: Need to animate the camera switch switchCamera(); - } @Override @@ -3279,17 +3283,19 @@ public class VideoModule implements CameraModule, @Override public void onPreviewUIDestroyed() { - stopPreview(); - } - - @Override - public void onButtonPause() { - pauseVideoRecording(); - } - @Override - public void onButtonContinue() { - resumeVideoRecording(); + if (mCameraDevice == null) { + return; + } + try { + if (mOpenCameraThread != null) { + mOpenCameraThread.join(); + mOpenCameraThread = null; + } + } catch (InterruptedException ex) { + // ignore + } + stopPreview(); } private void updatePowerMode() { @@ -3311,9 +3317,14 @@ public class VideoModule implements CameraModule, || mFaceDetectionStarted) return; if (mParameters.getMaxNumDetectedFaces() > 0) { mFaceDetectionStarted = true; - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; - mUI.onStartFaceDetection(mCameraDisplayOrientation, - (info.facing == CameraInfo.CAMERA_FACING_FRONT)); + final CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.onStartFaceDetection(mCameraDisplayOrientation, + (info.facing == CameraInfo.CAMERA_FACING_FRONT)); + } + }); mCameraDevice.setFaceDetectionCallback(mHandler, mUI); Log.d(TAG, "start face detection Video "+mParameters.getMaxNumDetectedFaces()); mCameraDevice.startFaceDetection(); @@ -3339,12 +3350,15 @@ public class VideoModule implements CameraModule, @Override public void showPreviewCover() { + stopFaceDetection(); + mUI.getFocusRing().stopFocusAnimations(); mUI.showPreviewCover(); } @Override public void hidePreviewCover() { mUI.hidePreviewCover(); + startFaceDetection(); } @Override diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index 6eff51eeb..a21b327a8 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -26,6 +26,7 @@ import android.graphics.Color; import android.graphics.Point; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; +import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.hardware.Camera.Face; import android.util.Log; @@ -57,9 +58,11 @@ import com.android.camera.ui.FaceView; import com.android.camera.ui.ListSubMenu; import com.android.camera.ui.ModuleSwitcher; import com.android.camera.ui.PieRenderer; +import com.android.camera.ui.RecordingTime; import com.android.camera.ui.RenderOverlay; -import com.android.camera.ui.RotateImageView; +import com.android.camera.ui.ReversibleLinearLayout; import com.android.camera.ui.RotateLayout; +import com.android.camera.ui.RotateImageView; import com.android.camera.ui.RotateTextToast; import com.android.camera.ui.ZoomRenderer; import com.android.camera.ui.focus.FocusRing; @@ -82,10 +85,8 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, private View mReviewDoneButton; private View mReviewPlayButton; private ShutterButton mShutterButton; - private PauseButton mPauseButton; - private TextView mRecordingTimeView; + private RecordingTime mRecordingTime; private LinearLayout mLabelsLinearLayout; - private View mTimeLapseLabel; private RenderOverlay mRenderOverlay; private PieRenderer mPieRenderer; private VideoMenu mVideoMenu; @@ -94,7 +95,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, private PreviewGestures mGestures; private View mMenuButton; private OnScreenIndicators mOnScreenIndicators; - private RotateLayout mRecordingTimeRect; private boolean mRecordingStarted = false; private VideoController mController; private int mZoomMax; @@ -102,7 +102,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, private ImageView mThumbnail; private boolean mOrientationResize; private boolean mPrevOrientationResize; - private boolean mIsTimeLapse = false; private RotateLayout mMenuLayout; private RotateLayout mSubMenuLayout; private LinearLayout mPreviewMenuLayout; @@ -213,6 +212,7 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, } }); + mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring); mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); @@ -236,9 +236,8 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, }); initializeMiscControls(); - initializeOverlay(); initializeControlByIntent(); - initializePauseButton(); + initializeRecordingTime(); ViewStub faceViewStub = (ViewStub) mRootView .findViewById(R.id.face_view_stub); @@ -249,10 +248,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, } mOrientationResize = false; mPrevOrientationResize = false; - - mCameraControls.disableSceneModes(); - - ((ViewGroup)mRootView).removeView(mRecordingTimeRect); } public void cameraOrientationPreviewResize(boolean orientation){ @@ -264,15 +259,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, mSurfaceTextureSizeListener = listener; } - public void initializeSurfaceView() { - if (mSurfaceView == null) { - mSurfaceView = new SurfaceView(mActivity); - ((ViewGroup) mRootView).addView(mSurfaceView, 0); - mSurfaceHolder = mSurfaceView.getHolder(); - mSurfaceHolder.addCallback(this); - } - } - private void initializeControlByIntent() { mMenuButton = mRootView.findViewById(R.id.menu); mMenuButton.setOnClickListener(new OnClickListener() { @@ -282,9 +268,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, } }); - mOnScreenIndicators = new OnScreenIndicators(mActivity, - mRootView.findViewById(R.id.on_screen_indicators)); - mOnScreenIndicators.resetToDefault(); if (mController.isVideoCaptureIntent()) { mCameraControls.hideSwitcher(); mActivity.getLayoutInflater().inflate(R.layout.review_module_control, @@ -337,7 +320,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, mAspectRatio = ratio; } - mCameraControls.setPreviewRatio(mAspectRatio, false); layoutPreview((float) ratio); } @@ -535,28 +517,42 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, mSurfaceView.setVisibility(View.VISIBLE); } - private void initializeOverlay() { - mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); + private void initIndicators() { + mOnScreenIndicators = new OnScreenIndicators(mActivity, + mRootView.findViewById(R.id.on_screen_indicators)); + } + + public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs, + Camera.Parameters params, OnPreferenceChangedListener listener) { if (mPieRenderer == null) { mPieRenderer = new PieRenderer(mActivity); // mVideoMenu = new VideoMenu(mActivity, this, mPieRenderer); mPieRenderer.setPieListener(this); + mRenderOverlay.addRenderer(mPieRenderer); } if (mVideoMenu == null) { mVideoMenu = new VideoMenu(mActivity, this); + mVideoMenu.setListener(listener); } - mRenderOverlay.addRenderer(mPieRenderer); + mVideoMenu.initialize(prefGroup); + if (mZoomRenderer == null) { mZoomRenderer = new ZoomRenderer(mActivity); + mRenderOverlay.addRenderer(mZoomRenderer); } - mRenderOverlay.addRenderer(mZoomRenderer); + if (mGestures == null) { mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer); mRenderOverlay.setGestures(mGestures); } mGestures.setVideoMenu(mVideoMenu); + mGestures.setZoomEnabled(params.isZoomSupported()); mGestures.setRenderOverlay(mRenderOverlay); + mRenderOverlay.requestLayout(); + + initializeZoom(params); + mActivity.setPreviewGestures(mGestures); if (!mActivity.isSecureCamera()) { mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); @@ -572,10 +568,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, } } - public void setPreviewGesturesVideoUI() { - mActivity.setPreviewGestures(mGestures); - } - public boolean isCameraControlsAnimating() { return mCameraControls.isAnimating(); } @@ -584,31 +576,42 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, mVideoMenu.setListener(listener); } - private void initializeMiscControls() { - mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image); + // called from onResume but only the first time + public void initializeFirstTime() { + initIndicators(); + + // Initialize shutter button. mShutterButton.setImageResource(R.drawable.btn_new_shutter_video); mShutterButton.addOnShutterButtonListener(mController); + mShutterButton.setVisibility(View.VISIBLE); + + mCameraControls.disableSceneModes(); + } + + private void initializeMiscControls() { + mReviewImage = (ImageView) mRootView.findViewById(R.id.review_image); mShutterButton.requestFocus(); mShutterButton.enableTouch(true); - mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time); - mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect); - mTimeLapseLabel = mRootView.findViewById(R.id.time_lapse_label); + // The R.id.labels can only be found in phone layout. // That is, mLabelsLinearLayout should be null in tablet layout. mLabelsLinearLayout = (LinearLayout) mRootView.findViewById(R.id.labels); } - private void initializePauseButton() { - mPauseButton = (PauseButton) mRootView.findViewById(R.id.video_pause); - mPauseButton.setOnPauseButtonListener(this); + private void initializeRecordingTime() { + mRecordingTime = (RecordingTime) mRootView.findViewById(R.id.recording_time); + mRecordingTime.setPauseListener(this); } public void updateOnScreenIndicators(Parameters param, ComboPreferences prefs) { - mOnScreenIndicators.updateExposureOnScreenIndicator(param, - CameraSettings.readExposure(prefs)); - mOnScreenIndicators.updateFlashOnScreenIndicator(param.getFlashMode()); - boolean location = RecordLocationPreference.get(prefs); - mOnScreenIndicators.updateLocationIndicator(location); + if (param == null) { + return; + } + mOnScreenIndicators.updateExposureOnScreenIndicator(param, + CameraSettings.readExposure(prefs)); + mOnScreenIndicators.updateFlashOnScreenIndicator(param.getFlashMode()); + boolean location = RecordLocationPreference.get(prefs); + mOnScreenIndicators.updateLocationIndicator(location); } @@ -622,15 +625,11 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, mAspectRatio = (float)ratio; } - mCameraControls.setPreviewRatio(mAspectRatio, false); layoutPreview((float)ratio); } public void showTimeLapseUI(boolean enable) { - if (mTimeLapseLabel != null) { - mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE); - } - mIsTimeLapse = enable; + mRecordingTime.showTimeLapse(enable); } public void dismissPopup(boolean topLevelOnly) { @@ -865,28 +864,37 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, public void showRecordingUI(boolean recording) { mRecordingStarted = recording; - mMenuButton.setVisibility(recording ? View.GONE : View.VISIBLE); mOnScreenIndicators.setVisibility(recording ? View.GONE : View.VISIBLE); if (recording) { mShutterButton.setImageResource(R.drawable.shutter_button_video_stop); mCameraControls.hideSwitcher(); - mRecordingTimeView.setText(""); - ((ViewGroup)mRootView).addView(mRecordingTimeRect); } else { mShutterButton.setImageResource(R.drawable.btn_new_shutter_video); if (!mController.isVideoCaptureIntent()) { mCameraControls.showSwitcher(); } - ((ViewGroup)mRootView).removeView(mRecordingTimeRect); + stopRecordingTimer(); } } + public void startRecordingTimer(int frameRate, long frameInterval, long durationMs) { + mRecordingTime.start(frameRate, frameInterval, durationMs); + } + + public void stopRecordingTimer() { + mRecordingTime.stop(); + } + + public long getRecordingTime() { + return mRecordingTime.getTime(); + } + public void hideUIwhileRecording() { - mCameraControls.hideCameraSettings(); + mCameraControls.setMenuAndSwitcherEnabled(false); } public void showUIafterRecording() { - mCameraControls.showCameraSettings(); + mCameraControls.setMenuAndSwitcherEnabled(true); } public void showReviewImage(Bitmap bitmap) { @@ -899,14 +907,14 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, CameraUtil.fadeIn(mReviewDoneButton); CameraUtil.fadeIn(mReviewPlayButton); mReviewImage.setVisibility(View.VISIBLE); - mMenuButton.setVisibility(View.GONE); + mCameraControls.hideCameraSettings(); mOnScreenIndicators.setVisibility(View.GONE); } public void hideReviewUI() { mReviewImage.setVisibility(View.GONE); mShutterButton.setEnabled(true); - mMenuButton.setVisibility(View.VISIBLE); + mCameraControls.hideCameraSettings(); mOnScreenIndicators.setVisibility(View.VISIBLE); CameraUtil.fadeOut(mReviewDoneButton); CameraUtil.fadeOut(mReviewPlayButton); @@ -937,16 +945,11 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, setShowMenu(previewFocused); } - public void initializePopup(PreferenceGroup pref) { - mVideoMenu.initialize(pref); - } - public void initializeZoom(Parameters param) { - if (param == null || !param.isZoomSupported()) { + if (param == null || !param.isZoomSupported() || mZoomRenderer == null) { mGestures.setZoomEnabled(false); return; } - mGestures.setZoomEnabled(true); mZoomMax = param.getMaxZoom(); mZoomRatios = param.getZoomRatios(); // Currently we use immediate zoom for fast zooming to get better UX and @@ -969,14 +972,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, return mShutterButton; } - public void setRecordingTime(String text) { - mRecordingTimeView.setText(text); - } - - public void setRecordingTimeTextColor(int color) { - mRecordingTimeView.setTextColor(color); - } - public boolean isVisible() { return mCameraControls.getVisibility() == View.VISIBLE; } @@ -1031,6 +1026,8 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, RectF r = new RectF(mSurfaceView.getLeft(), mSurfaceView.getTop(), mSurfaceView.getRight(), mSurfaceView.getBottom()); + mController.onPreviewRectChanged(CameraUtil.rectFToRect(r)); + onPreviewRectChanged(r); } @@ -1053,26 +1050,16 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, return mRootView; } - @Override + @Override public void onButtonPause() { - mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( - R.drawable.ic_pausing_indicator, 0, 0, 0); mController.onButtonPause(); } @Override public void onButtonContinue() { - mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( - R.drawable.ic_recording_indicator, 0, 0, 0); mController.onButtonContinue(); } - public void resetPauseButton() { - mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( - R.drawable.ic_recording_indicator, 0, 0, 0); - mPauseButton.setPaused(false); - } - public void setPreference(String key, String value) { mVideoMenu.setPreference(key, value); } @@ -1083,14 +1070,9 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, mMenuLayout.setOrientation(orientation, animation); if (mSubMenuLayout != null) mSubMenuLayout.setOrientation(orientation, animation); - if (mRecordingTimeRect != null) { - if (orientation == 180) { - mRecordingTimeRect.setOrientation(0, false); - mRecordingTimeView.setRotation(180); - } else { - mRecordingTimeView.setRotation(0); - mRecordingTimeRect.setOrientation(orientation, false); - } + + if (mRecordingTime != null) { + mRecordingTime.setOrientation(orientation); } if (mPreviewMenuLayout != null) { ViewGroup vg = (ViewGroup) mPreviewMenuLayout.getChildAt(0); diff --git a/src/com/android/camera/WideAnglePanoramaUI.java b/src/com/android/camera/WideAnglePanoramaUI.java index 4d48d9501..547836850 100644 --- a/src/com/android/camera/WideAnglePanoramaUI.java +++ b/src/com/android/camera/WideAnglePanoramaUI.java @@ -24,6 +24,7 @@ import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.SurfaceTexture; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; @@ -654,6 +655,9 @@ public class WideAnglePanoramaUI extends BaseUI implements } layoutPreview(); - mCameraControls.setPreviewRatio(mAspectRatio, false); + + RectF r = new RectF(mTextureView.getLeft(), mTextureView.getTop(), + mTextureView.getRight(), mTextureView.getBottom()); + onPreviewRectChanged(r); } } diff --git a/src/com/android/camera/ui/CameraControls.java b/src/com/android/camera/ui/CameraControls.java index a5f7db685..740a07ea1 100644 --- a/src/com/android/camera/ui/CameraControls.java +++ b/src/com/android/camera/ui/CameraControls.java @@ -21,15 +21,17 @@ import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.util.Log; -import android.view.MotionEvent; +import android.util.Pair; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; @@ -60,17 +62,18 @@ public class CameraControls extends RotatableLayout { private View mVideoShutter; private ModuleSwitcher mSwitcher; private View mTsMakeupSwitcher; - private View mPreview; + private View mThumbnail; private View mAutoHdrNotice; private HistogramView mHistogramView; private ArrowTextView mRefocusToast; + private View mFrontBackSwitcher; + private View mMenu; private View mReviewDoneButton; private View mReviewCancelButton; private View mReviewRetakeButton; private final List mViews = new ArrayList<>(); - private final List mFreeList = new ArrayList<>(); private static final int WIDTH_GRID = 5; private static final int HEIGHT_GRID = 7; @@ -102,7 +105,9 @@ public class CameraControls extends RotatableLayout { } @Override public void onAnimationEnd(Animator animation) { + setChildrenVisibility(mBottomBar, false); if (mFullyHidden) { + setChildrenVisibility(mTopBar, false); setVisibility(View.INVISIBLE); } } @@ -111,7 +116,9 @@ public class CameraControls extends RotatableLayout { AnimatorListener inlistener = new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { + setChildrenVisibility(mBottomBar, true); if (mFullyHidden) { + setChildrenVisibility(mTopBar, true); setVisibility(View.VISIBLE); mFullyHidden = false; } @@ -122,14 +129,26 @@ public class CameraControls extends RotatableLayout { } }; + private void setChildrenVisibility(ViewGroup parent, boolean visible) { + for (int i = 0; i < parent.getChildCount(); i++) { + View v = parent.getChildAt(i); + if (v.getVisibility() != View.GONE) { + v.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + } + } + } + public CameraControls(Context context, AttributeSet attrs) { super(context, attrs); - mRefocusToast = new ArrowTextView(context); addView(mRefocusToast); setClipChildren(false); setMeasureAllChildren(true); + + Pair margins = CameraUtil.calculateMargins((Activity)context); + mTopMargin = margins.first; + mBottomMargin = margins.second; } public CameraControls(Context context) { @@ -142,12 +161,15 @@ public class CameraControls extends RotatableLayout { public void enableTouch(boolean enable) { Log.d(TAG, "ENABLE TOUCH " + enable + " mViews.size=" + mViews.size()); - for (View v : mViews) { - if (v.getVisibility() != View.GONE) { - if (enable) { - v.setPressed(false); + + synchronized (mViews) { + for (View v : mViews) { + if (v.getVisibility() != View.GONE) { + if (enable) { + v.setPressed(false); + } + v.setEnabled(enable); } - v.setEnabled(enable); } } @@ -158,11 +180,13 @@ public class CameraControls extends RotatableLayout { } public void removeFromViewList(View view) { - synchronized (mFreeList) { + synchronized (mViews) { if (view == null || !mViews.contains(view)) { return; } - mFreeList.add(view); + view.setVisibility(View.GONE); + removeView(view); + mViews.remove(view); requestLayout(); } } @@ -176,26 +200,30 @@ public class CameraControls extends RotatableLayout { mShutter = (ShutterButton) findViewById(R.id.shutter_button); mVideoShutter = findViewById(R.id.video_button); mTsMakeupSwitcher = findViewById(R.id.ts_makeup_switcher); - mPreview = findViewById(R.id.preview_thumb); + mThumbnail = findViewById(R.id.preview_thumb); mRemainingPhotos = (LinearLayout) findViewById(R.id.remaining_photos); mRemainingPhotosText = (TextView) findViewById(R.id.remaining_photos_text); mAutoHdrNotice = (TextView) findViewById(R.id.auto_hdr_notice); mHistogramView = (HistogramView) findViewById(R.id.histogram); + mFrontBackSwitcher = findViewById(R.id.front_back_switcher); + mMenu = findViewById(R.id.menu); if (!TsMakeupManager.HAS_TS_MAKEUP) { mTopBar.removeView(mTsMakeupSwitcher); } - for (int i = 0; i < mTopBar.getChildCount(); i++) { - mViews.add(mTopBar.getChildAt(i)); - } + synchronized (mViews) { + for (int i = 0; i < mTopBar.getChildCount(); i++) { + mViews.add(mTopBar.getChildAt(i)); + } - for (int i = 0; i < mBottomBar.getChildCount(); i++) { - mViews.add(mBottomBar.getChildAt(i)); - } + for (int i = 0; i < mBottomBar.getChildCount(); i++) { + mViews.add(mBottomBar.getChildAt(i)); + } - mViews.add(mAutoHdrNotice); - mViews.add(mHistogramView); + mViews.add(mAutoHdrNotice); + mViews.add(mHistogramView); + } mShutter.addOnShutterButtonListener(mShutterListener); @@ -222,13 +250,13 @@ public class CameraControls extends RotatableLayout { public void hideSwitcher() { if (mSwitcher != null) { mSwitcher.closePopup(); - mSwitcher.setVisibility(View.INVISIBLE); + mSwitcher.setEnabled(false); } } public void showSwitcher() { if (mSwitcher != null) { - mSwitcher.setVisibility(View.VISIBLE); + mSwitcher.setEnabled(true); } } @@ -271,26 +299,9 @@ public class CameraControls extends RotatableLayout { @Override public void onLayout(boolean changed, int l, int t, int r, int b) { - synchronized (mFreeList) { - if (mFreeList.size() > 0) { - for (View v : mFreeList) { - v.setVisibility(View.GONE); - removeView(v); - mViews.remove(v); - } - } - } + Log.d(TAG, String.format("onLayout changed=%b l=%d t=%d r=%d b=%d", changed, l, t, r, b)); - // As l,t,r,b are positions relative to parents, we need to convert them - // to child's coordinates - r = r - l; - b = b - t; - l = 0; - t = 0; - for (int i = 0; i < getChildCount(); i++) { - View v = getChildAt(i); - v.layout(l, t, r, b); - } + super.onLayout(changed, l, t, r, b); ViewGroup.LayoutParams lpTop = mTopBar.getLayoutParams(); lpTop.height = mTopMargin; @@ -346,6 +357,19 @@ public class CameraControls extends RotatableLayout { }); } + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + mTopBar.setTranslationX(0); + mTopBar.setTranslationY(0); + mBottomBar.setTranslationX(0); + mBottomBar.setTranslationY(0); + mHidden = false; + mFullyHidden = false; + setChildrenVisibility(mTopBar, true); + setChildrenVisibility(mBottomBar, true); + } + private void setLocation(int w, int h) { int rotation = getUnifiedRotation(); layoutToast(mRefocusToast, w, h, rotation); @@ -452,6 +476,11 @@ public class CameraControls extends RotatableLayout { mAnimator.start(); } + public void setMenuAndSwitcherEnabled(boolean enable) { + mMenu.setEnabled(enable); + mFrontBackSwitcher.setEnabled(enable); + } + public void hideUI(boolean toBlack) { if (mHidden) { return; @@ -493,10 +522,10 @@ public class CameraControls extends RotatableLayout { } private void layoutRemaingPhotos() { - int rl = mPreview.getLeft(); - int rt = mPreview.getTop(); - int rr = mPreview.getRight(); - int rb = mPreview.getBottom(); + int rl = mThumbnail.getLeft(); + int rt = mThumbnail.getTop(); + int rr = mThumbnail.getRight(); + int rb = mThumbnail.getBottom(); int w = mRemainingPhotos.getMeasuredWidth(); int h = mRemainingPhotos.getMeasuredHeight(); int m = getResources().getDimensionPixelSize(R.dimen.remaining_photos_margin); @@ -537,25 +566,14 @@ public class CameraControls extends RotatableLayout { return !mHidden; } - public void setMargins(int top, int bottom) { - mTopMargin = top; - mBottomMargin = bottom; - } - - private void setBarsBackground(int resId) { - mTopBar.setBackgroundResource(resId); - mBottomBar.setBackgroundResource(resId); - } - - public void setPreviewRatio(float ratio, boolean panorama) { - int r = CameraUtil.determineRatio(ratio); + public void setPreviewRect(RectF rectL) { + int r = CameraUtil.determineRatio(Math.round(rectL.width()), Math.round(rectL.height())); mPreviewRatio = r; if (mPreviewRatio == CameraUtil.RATIO_4_3 && mTopMargin != 0) { - setBarsBackground(R.drawable.camera_controls_bg_opaque); + mBottomBar.setBackgroundResource(R.drawable.camera_controls_bg_opaque); } else { - setBarsBackground(R.drawable.camera_controls_bg_translucent); + mBottomBar.setBackgroundResource(R.drawable.camera_controls_bg_translucent); } - requestLayout(); } public void showRefocusToast(boolean show) { @@ -577,11 +595,13 @@ public class CameraControls extends RotatableLayout { public void setOrientation(int orientation, boolean animation) { mOrientation = orientation; - for (View v : mViews) { - if (v instanceof RotateImageView) { - ((RotateImageView) v).setOrientation(orientation, animation); - } else if (v instanceof HistogramView) { - ((HistogramView) v).setRotation(-orientation); + synchronized (mViews) { + for (View v : mViews) { + if (v instanceof RotateImageView) { + ((RotateImageView) v).setOrientation(orientation, animation); + } else if (v instanceof HistogramView) { + ((HistogramView) v).setRotation(-orientation); + } } } layoutRemaingPhotos(); diff --git a/src/com/android/camera/ui/ModuleSwitcher.java b/src/com/android/camera/ui/ModuleSwitcher.java index d51edb19a..90ba05484 100644 --- a/src/com/android/camera/ui/ModuleSwitcher.java +++ b/src/com/android/camera/ui/ModuleSwitcher.java @@ -159,7 +159,7 @@ public class ModuleSwitcher extends RotateImageView { // Closes the popup window when touch outside of it - when looses focus popup.setOutsideTouchable(true); popup.setFocusable(true); - popup.setBackgroundDrawable(new ColorDrawable(Color.WHITE)); + popup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); popup.setAnimationStyle(android.R.style.Animation_Dialog); return popup; } diff --git a/src/com/android/camera/ui/RecordingTime.java b/src/com/android/camera/ui/RecordingTime.java new file mode 100644 index 000000000..6e07fa69b --- /dev/null +++ b/src/com/android/camera/ui/RecordingTime.java @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.camera.ui; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.transition.*; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import com.android.camera.PauseButton; + +import org.codeaurora.snapcam.R; + +public class RecordingTime extends RotateLayout implements PauseButton.OnPauseButtonListener { + + private static final String TAG = "CAM_" + RecordingTime.class.getSimpleName(); + + private PauseButton mPauseButton; + private TextView mRecordingTimeText; + private TextView mTimeLapseLabel; + private ReversibleLinearLayout mRecordingTimeContainer; + + private PauseButton.OnPauseButtonListener mPauseListener; + + private boolean mPaused = false; + private boolean mStarted = false; + private boolean mTimeLapse = false; + + private long mRecordingStartTime; + private long mRecordingTotalTime; + + private int mFrameRate = 0; + private long mInterval = 0; + private long mDurationMs = 0; + private boolean mRecordingTimeCountsDown = false; + + private static final int UPDATE_RECORD_TIME = 0; + + private final Handler mHandler = new Handler() { + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case UPDATE_RECORD_TIME: + updateRecordingTime(); + break; + default: + break; + } + } + }; + + public RecordingTime(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mPauseButton = (PauseButton) findViewById(R.id.video_pause); + mRecordingTimeText = (TextView) findViewById(R.id.recording_time_text); + mRecordingTimeContainer = (ReversibleLinearLayout) findViewById(R.id.recording_time_container); + mTimeLapseLabel = (TextView) findViewById(R.id.time_lapse_label); + mPauseButton.setOnPauseButtonListener(this); + setAlpha(0.0f); + } + + public void setOrientation(int orientation) { + if (mRecordingTimeText != null) { + Log.d(TAG, "orientation=" + orientation); + setRotation(orientation); + float topBar = getContext().getResources().getDimension(R.dimen.preview_top_margin); + + if (orientation == 0 || orientation == 270) { + mRecordingTimeContainer.setOrder(ReversibleLinearLayout.FORWARD); + } else { + mRecordingTimeContainer.setOrder(ReversibleLinearLayout.REVERSE); + } + + if (orientation == 0 || orientation == 180) { + setTranslationX(0); + setTranslationY(0); + mRecordingTimeText.setRotation(0); + if (mTimeLapse) { + mTimeLapseLabel.setRotation(0); + } + } else { + setTranslationX(-topBar); + setTranslationY(topBar); + mRecordingTimeText.setRotation(180); + if (mTimeLapse) { + mTimeLapseLabel.setRotation(180); + } + } + } + } + + @Override + public void onButtonPause() { + mPaused = true; + mRecordingTotalTime += SystemClock.uptimeMillis() - mRecordingStartTime; + + mRecordingTimeText.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_pausing_indicator, 0, 0, 0); + if (mPauseListener != null) { + mPauseListener.onButtonPause(); + } + } + + @Override + public void onButtonContinue() { + mPaused = false; + mRecordingStartTime = SystemClock.uptimeMillis(); + + mRecordingTimeText.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_recording_indicator, 0, 0, 0); + if (mPauseListener != null) { + mPauseListener.onButtonContinue(); + } + updateRecordingTime(); + } + + public void reset() { + mRecordingTimeText.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_recording_indicator, 0, 0, 0); + mStarted = false; + mPauseButton.setPaused(false); + mPaused = false; + mRecordingTimeText.setText(""); + } + + public void start() { + start(0, 0, 0); + } + + public void start(int frameRate, long frameInterval, long durationMs) { + reset(); + mFrameRate = frameRate; + mInterval = frameInterval; + mDurationMs = durationMs; + mRecordingTotalTime = 0L; + mRecordingStartTime = SystemClock.uptimeMillis(); + mStarted = true; + animate().alpha(1.0f).withStartAction(new Runnable() { + @Override + public void run() { + updateRecordingTime(); + setVisibility(View.VISIBLE); + } + }); + + Log.d(TAG, "started: frameRate=" + frameRate + " frameInterval=" + frameInterval + " maxDuration=" + durationMs); + } + + public void stop() { + mStarted = false; + animate().alpha(0.0f).withEndAction(new Runnable() { + @Override + public void run() { + setVisibility(View.GONE); + } + }); + } + + public void showTimeLapse(boolean show) { + mTimeLapse = show; + mTimeLapseLabel.setVisibility(show ? View.VISIBLE : View.GONE); + } + + public long getTime() { + return mPaused ? mRecordingTotalTime : + SystemClock.uptimeMillis() - mRecordingStartTime + mRecordingTotalTime; + } + + public void setPauseListener(PauseButton.OnPauseButtonListener listener) { + mPauseListener = listener; + } + + private void updateRecordingTime() { + if (!mStarted || mPaused) { + return; + } + + long now = SystemClock.uptimeMillis(); + long delta = now - mRecordingStartTime + mRecordingTotalTime; + + // Starting a minute before reaching the max duration + // limit, we'll countdown the remaining time instead. + boolean countdownRemainingTime = (mDurationMs != 0 + && delta >= mDurationMs - 60000); + + long deltaAdjusted = delta; + if (countdownRemainingTime) { + deltaAdjusted = Math.max(0, mDurationMs - deltaAdjusted) + 999; + } + String text; + + long targetNextUpdateDelay; + if (mInterval <= 0) { + text = millisecondToTimeString(deltaAdjusted, false); + targetNextUpdateDelay = 1000; + } else { + // The length of time lapse video is different from the length + // of the actual wall clock time elapsed. Display the video length + // only in format hh:mm:ss.dd, where dd are the centi seconds. + text = millisecondToTimeString(getTimeLapseVideoLength(delta), true); + targetNextUpdateDelay = mInterval; + } + + mRecordingTimeText.setText(text); + + if (mRecordingTimeCountsDown != countdownRemainingTime) { + // Avoid setting the color on every update, do it only + // when it needs changing. + mRecordingTimeCountsDown = countdownRemainingTime; + + int color = getContext().getResources().getColor(countdownRemainingTime + ? R.color.recording_time_remaining_text + : R.color.recording_time_elapsed_text); + + mRecordingTimeText.setTextColor(color); + } + + long actualNextUpdateDelay = targetNextUpdateDelay - (delta % targetNextUpdateDelay); + mHandler.sendEmptyMessageDelayed( + UPDATE_RECORD_TIME, actualNextUpdateDelay); + } + + private long getTimeLapseVideoLength(long deltaMs) { + // For better approximation calculate fractional number of frames captured. + // This will update the video time at a higher resolution. + double numberOfFrames = (double) deltaMs / mInterval; + return (long) (numberOfFrames / mFrameRate * 1000); + } + + private static String millisecondToTimeString(long milliSeconds, boolean displayCentiSeconds) { + long seconds = milliSeconds / 1000; // round down to compute seconds + long minutes = seconds / 60; + long hours = minutes / 60; + long remainderMinutes = minutes - (hours * 60); + long remainderSeconds = seconds - (minutes * 60); + + StringBuilder timeStringBuilder = new StringBuilder(); + + // Hours + if (hours > 0) { + if (hours < 10) { + timeStringBuilder.append('0'); + } + timeStringBuilder.append(hours); + + timeStringBuilder.append(':'); + } + + // Minutes + if (remainderMinutes < 10) { + timeStringBuilder.append('0'); + } + timeStringBuilder.append(remainderMinutes); + timeStringBuilder.append(':'); + + // Seconds + if (remainderSeconds < 10) { + timeStringBuilder.append('0'); + } + timeStringBuilder.append(remainderSeconds); + + // Centi seconds + if (displayCentiSeconds) { + timeStringBuilder.append('.'); + long remainderCentiSeconds = (milliSeconds - seconds * 1000) / 10; + if (remainderCentiSeconds < 10) { + timeStringBuilder.append('0'); + } + timeStringBuilder.append(remainderCentiSeconds); + } + + return timeStringBuilder.toString(); + } +} diff --git a/src/com/android/camera/ui/ReversibleLinearLayout.java b/src/com/android/camera/ui/ReversibleLinearLayout.java new file mode 100644 index 000000000..df9e921f0 --- /dev/null +++ b/src/com/android/camera/ui/ReversibleLinearLayout.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.camera.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + +/** + * A LinearLayout which can have it's elements reversed + */ +public class ReversibleLinearLayout extends LinearLayout { + + public static final int FORWARD = 0; + public static final int REVERSE = 1; + + private int mState = FORWARD; + + public ReversibleLinearLayout(Context context) { + super(context); + } + + public ReversibleLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setOrder(int state) { + if (state < 0 || state > 1 || state == mState) { + return; + } + for(int k = getChildCount()-1 ; k >= 0 ; k--) + { + View item = getChildAt(k); + removeViewAt(k); + addView(item); + } + mState = state; + } + + public int getOrder() { + return mState; + } +} diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java index 653cc9a8d..5af83bcf9 100644 --- a/src/com/android/camera/util/CameraUtil.java +++ b/src/com/android/camera/util/CameraUtil.java @@ -44,6 +44,7 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; +import android.util.Pair; import android.util.TypedValue; import android.view.Display; import android.view.OrientationEventListener; @@ -83,7 +84,7 @@ import java.util.TreeSet; * Collection of utility functions used in this package. */ public class CameraUtil { - private static final String TAG = "Util"; + private static final String TAG = "CAM_Util"; // For calculate the best fps range for still image capture. private final static int MAX_PREVIEW_FPS_TIMES_1000 = 400000; @@ -1285,6 +1286,17 @@ public class CameraUtil { return ".3gp"; } + public static Pair calculateMargins(Activity activity) { + Point size = new Point(); + activity.getWindowManager().getDefaultDisplay().getRealSize(size); + int l = size.x > size.y ? size.x : size.y; + int tm = activity.getResources().getDimensionPixelSize(R.dimen.preview_top_margin); + int bm = activity.getResources().getDimensionPixelSize(R.dimen.preview_bottom_margin); + int top = l / 4 * tm / (tm + bm); + int bottom = l / 4 - top; + return Pair.create(top, bottom); + } + /** * Compares two {@code Size}s based on their areas. */ -- cgit v1.2.3