From f886863e6d8024ef6d2c2712aa518a761fff6b9d Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Wed, 9 Nov 2016 01:17:49 -0800 Subject: snap: Better capture animation * Modified version from Camera2 * Also adjust the callbacks for better response Change-Id: I85ac23cdf35d9e2625799dd64b9f86aef103be7b --- src/com/android/camera/AnimationManager.java | 182 --------------------- src/com/android/camera/BaseUI.java | 14 ++ src/com/android/camera/CaptureUI.java | 4 + src/com/android/camera/PhotoModule.java | 13 +- src/com/android/camera/PhotoUI.java | 10 +- src/com/android/camera/VideoModule.java | 7 +- src/com/android/camera/VideoUI.java | 44 +---- .../android/camera/ui/CaptureAnimationOverlay.java | 148 +++++++++++++++++ 8 files changed, 185 insertions(+), 237 deletions(-) delete mode 100644 src/com/android/camera/AnimationManager.java create mode 100644 src/com/android/camera/ui/CaptureAnimationOverlay.java (limited to 'src/com') diff --git a/src/com/android/camera/AnimationManager.java b/src/com/android/camera/AnimationManager.java deleted file mode 100644 index 5397ae02b..000000000 --- a/src/com/android/camera/AnimationManager.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source 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; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.view.View; - -/** - * Class to handle animations. - */ - -public class AnimationManager { - - public static final float FLASH_ALPHA_START = 0.3f; - public static final float FLASH_ALPHA_END = 0f; - public static final int FLASH_DURATION = 300; - - public static final int SHRINK_DURATION = 400; - public static final int HOLD_DURATION = 2500; - public static final int SLIDE_DURATION = 1100; - - private ObjectAnimator mFlashAnim; - private AnimatorSet mCaptureAnimator; - - /** - * Starts capture animation. - * @param view a thumbnail view that shows a picture captured and gets animated - */ - public void startCaptureAnimation(final View view) { - if (mCaptureAnimator != null && mCaptureAnimator.isStarted()) { - mCaptureAnimator.cancel(); - } - View parentView = (View) view.getParent(); - float slideDistance = (float) (parentView.getWidth() - view.getLeft()); - - float scaleX = ((float) parentView.getWidth()) / ((float) view.getWidth()); - float scaleY = ((float) parentView.getHeight()) / ((float) view.getHeight()); - float scale = scaleX > scaleY ? scaleX : scaleY; - - int centerX = view.getLeft() + view.getWidth() / 2; - int centerY = view.getTop() + view.getHeight() / 2; - - ObjectAnimator slide = ObjectAnimator.ofFloat(view, "translationX", 0f, slideDistance) - .setDuration(AnimationManager.SLIDE_DURATION); - slide.setStartDelay(AnimationManager.SHRINK_DURATION + AnimationManager.HOLD_DURATION); - - ObjectAnimator translateY = ObjectAnimator.ofFloat(view, "translationY", - parentView.getHeight() / 2 - centerY, 0f) - .setDuration(AnimationManager.SHRINK_DURATION); - translateY.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animator) { - // Do nothing. - } - - @Override - public void onAnimationEnd(Animator animator) { - view.setClickable(true); - } - - @Override - public void onAnimationCancel(Animator animator) { - // Do nothing. - } - - @Override - public void onAnimationRepeat(Animator animator) { - // Do nothing. - } - }); - - mCaptureAnimator = new AnimatorSet(); - mCaptureAnimator.playTogether( - ObjectAnimator.ofFloat(view, "scaleX", scale, 1f) - .setDuration(AnimationManager.SHRINK_DURATION), - ObjectAnimator.ofFloat(view, "scaleY", scale, 1f) - .setDuration(AnimationManager.SHRINK_DURATION), - ObjectAnimator.ofFloat(view, "translationX", - parentView.getWidth() / 2 - centerX, 0f) - .setDuration(AnimationManager.SHRINK_DURATION), - translateY, - slide); - mCaptureAnimator.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animator) { - view.setClickable(false); - view.setVisibility(View.VISIBLE); - } - - @Override - public void onAnimationEnd(Animator animator) { - view.setScaleX(1f); - view.setScaleX(1f); - view.setTranslationX(0f); - view.setTranslationY(0f); - view.setVisibility(View.INVISIBLE); - mCaptureAnimator.removeAllListeners(); - mCaptureAnimator = null; - } - - @Override - public void onAnimationCancel(Animator animator) { - view.setVisibility(View.INVISIBLE); - } - - @Override - public void onAnimationRepeat(Animator animator) { - // Do nothing. - } - }); - mCaptureAnimator.start(); - } - - /** - * Starts flash animation. - * @params flashOverlay the overlay that will animate on alpha to make the flash impression - */ - public void startFlashAnimation(final View flashOverlay) { - // End the previous animation if the previous one is still running - if (mFlashAnim != null && mFlashAnim.isRunning()) { - mFlashAnim.cancel(); - } - // Start new flash animation. - mFlashAnim = ObjectAnimator.ofFloat(flashOverlay, "alpha", - AnimationManager.FLASH_ALPHA_START, AnimationManager.FLASH_ALPHA_END); - mFlashAnim.setDuration(AnimationManager.FLASH_DURATION); - mFlashAnim.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animator) { - flashOverlay.setVisibility(View.VISIBLE); - } - - @Override - public void onAnimationEnd(Animator animator) { - flashOverlay.setAlpha(0f); - flashOverlay.setVisibility(View.GONE); - mFlashAnim.removeAllListeners(); - mFlashAnim = null; - } - - @Override - public void onAnimationCancel(Animator animator) { - // Do nothing. - } - - @Override - public void onAnimationRepeat(Animator animator) { - // Do nothing. - } - }); - mFlashAnim.start(); - } - - /** - * Cancels on-going flash animation and capture animation, if any. - */ - public void cancelAnimations() { - // End the previous animation if the previous one is still running - if (mFlashAnim != null && mFlashAnim.isRunning()) { - mFlashAnim.cancel(); - } - if (mCaptureAnimator != null && mCaptureAnimator.isStarted()) { - mCaptureAnimator.cancel(); - } - } -} diff --git a/src/com/android/camera/BaseUI.java b/src/com/android/camera/BaseUI.java index 372106258..f48b2875a 100644 --- a/src/com/android/camera/BaseUI.java +++ b/src/com/android/camera/BaseUI.java @@ -1,10 +1,12 @@ package com.android.camera; import android.graphics.Point; +import android.graphics.RectF; import android.view.View; import android.view.ViewGroup; import com.android.camera.ui.CameraControls; +import com.android.camera.ui.CaptureAnimationOverlay; import com.android.camera.ui.ModuleSwitcher; import com.android.camera.util.CameraUtil; @@ -15,6 +17,7 @@ import org.codeaurora.snapcam.R; public abstract class BaseUI { protected final CameraControls mCameraControls; + protected final CaptureAnimationOverlay mCaptureOverlay; protected final View mPreviewCover; protected final CameraActivity mActivity; @@ -31,6 +34,7 @@ public abstract class BaseUI { mActivity.getLayoutInflater().inflate(layout, mRootView, true); mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls); + mCaptureOverlay = (CaptureAnimationOverlay) mRootView.findViewById(R.id.capture_overlay); mPreviewCover = mRootView.findViewById(R.id.preview_cover); Point size = new Point(); @@ -119,4 +123,14 @@ public abstract class BaseUI { } mCameraControls.setModuleIndex(module); } + + public void animateFlash(boolean shortFlash) { + if (mCaptureOverlay != null) { + mCaptureOverlay.startFlashAnimation(shortFlash); + } + } + + protected void onPreviewRectChanged(RectF rect) { + mCaptureOverlay.setPreviewRect(rect); + } } diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index afebab705..81aa2b086 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -25,6 +25,7 @@ import android.content.res.Resources; import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.AnimationDrawable; import android.hardware.Camera.Face; import android.media.ImageReader; @@ -182,6 +183,9 @@ public class CaptureUI extends BaseUI implements PreviewGestures.SingleTapListen // SurfaceHolder callbacks @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + RectF r = new RectF(mSurfaceView.getLeft(), mSurfaceView.getTop(), + mSurfaceView.getRight(), mSurfaceView.getBottom()); + CaptureUI.this.onPreviewRectChanged(r); Log.v(TAG, "surfaceChanged: width =" + width + ", height = " + height); } diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index d6f23e320..8085c40d6 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -1656,7 +1656,7 @@ public class PhotoModule // i.e. If monkey/a user swipes to the gallery during picture taking, // don't show animation if (!mIsImageCaptureIntent) { - mUI.animateFlash(); + mUI.animateFlash(mFocusManager.isZslEnabled()); } } @@ -1674,7 +1674,9 @@ public class PhotoModule mPostViewPictureCallbackTime = 0; mJpegImageData = null; - final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR); + final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR) || + (mSnapshotMode == CameraInfo.CAMERA_SUPPORT_MODE_ZSL); + if(mHistogramEnabled) { if (mSnapshotMode != CameraInfo.CAMERA_SUPPORT_MODE_ZSL) { mHistogramEnabled = false; @@ -1689,7 +1691,12 @@ public class PhotoModule } if (animateBefore) { - animateAfterShutter(); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + animateAfterShutter(); + } + }); } if (mCameraState == LONGSHOT) { diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index 5aeccac8f..38c6497b6 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -83,7 +83,6 @@ public class PhotoUI extends BaseUI implements PieListener, private static final String TAG = "CAM_UI"; private int mDownSampleFactor = 4; - private final AnimationManager mAnimationManager; private final FocusRing mFocusRing; private PhotoController mController; private PreviewGestures mGestures; @@ -126,7 +125,6 @@ public class PhotoUI extends BaseUI implements PieListener, private float mSurfaceTextureUncroppedHeight; private ImageView mThumbnail; - private View mFlashOverlay; private SurfaceTextureSizeChangedListener mSurfaceTextureSizeListener; private SurfaceView mSurfaceView = null; @@ -251,7 +249,6 @@ public class PhotoUI extends BaseUI implements PieListener, }); mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); - mFlashOverlay = mRootView.findViewById(R.id.flash_overlay); mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); mMenuButton = mRootView.findViewById(R.id.menu); @@ -266,7 +263,6 @@ public class PhotoUI extends BaseUI implements PieListener, } mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring); initIndicators(); - mAnimationManager = new AnimationManager(); mOrientationResize = false; mPrevOrientationResize = false; @@ -435,6 +431,8 @@ public class PhotoUI extends BaseUI implements PieListener, RectF r = new RectF(mSurfaceView.getLeft(), mSurfaceView.getTop(), mSurfaceView.getRight(), mSurfaceView.getBottom()); mController.onPreviewRectChanged(CameraUtil.rectFToRect(r)); + + onPreviewRectChanged(r); } @Override @@ -672,10 +670,6 @@ public class PhotoUI extends BaseUI implements PieListener, public void setCameraState(int state) { } - public void animateFlash() { - mAnimationManager.startFlashAnimation(mFlashOverlay); - } - public void enableGestures(boolean enable) { if (mGestures != null) { mGestures.setEnabled(enable); diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index 8f57777f7..56ae96de0 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -833,8 +833,7 @@ public class VideoModule implements CameraModule, // the preview. This will cause the preview flicker since the preview // will not be continuous for a short period of time. - mUI.animateFlash(); - mUI.animateCapture(); + mUI.animateFlash(false); } } mUI.showUIafterRecording(); @@ -2006,7 +2005,6 @@ public class VideoModule implements CameraModule, private boolean startVideoRecording() { Log.v(TAG, "startVideoRecording"); mStartRecPending = true; - mUI.cancelAnimations(); mUI.setSwipingEnabled(false); mUI.hideUIwhileRecording(); // When recording request is sent before starting preview, onPreviewFrame() @@ -3107,8 +3105,7 @@ public class VideoModule implements CameraModule, if (mParameters == null) return; if (CameraUtil.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { if (enabled) { - mUI.animateFlash(); - mUI.animateCapture(); + mUI.animateFlash(true); } else { mUI.showPreviewBorder(enabled); } diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index 4107af22d..6eff51eeb 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -24,6 +24,7 @@ import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Point; +import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.hardware.Camera.Parameters; import android.hardware.Camera.Face; @@ -99,7 +100,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, private int mZoomMax; private List mZoomRatios; private ImageView mThumbnail; - private View mFlashOverlay; private boolean mOrientationResize; private boolean mPrevOrientationResize; private boolean mIsTimeLapse = false; @@ -112,7 +112,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, private int mMaxPreviewHeight = 0; private float mAspectRatio = 4f / 3f; private boolean mAspectRatioResize; - private final AnimationManager mAnimationManager; private int mPreviewOrientation = -1; private int mOrientation; @@ -215,7 +214,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, }); mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring); - mFlashOverlay = mRootView.findViewById(R.id.flash_overlay); mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); mMuteButton = (RotateImageView)mRootView.findViewById(R.id.mute_button); @@ -249,7 +247,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, mFaceView = (FaceView) mRootView.findViewById(R.id.face_view); setSurfaceTextureSizeChangedListener(mFaceView); } - mAnimationManager = new AnimationManager(); mOrientationResize = false; mPrevOrientationResize = false; @@ -456,41 +453,6 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, } } - /** - * Starts a flash animation - */ - public void animateFlash() { - mAnimationManager.startFlashAnimation(mFlashOverlay); - } - - /** - * Starts a capture animation - */ - public void animateCapture() { - Bitmap bitmap = null; - animateCapture(bitmap); - } - - /** - * Starts a capture animation - * @param bitmap the captured image that we shrink and slide in the animation - */ - public void animateCapture(Bitmap bitmap) { - if (bitmap == null) { - Log.e(TAG, "No valid bitmap for capture animation."); - return; - } - mActivity.updateThumbnail(bitmap); - mAnimationManager.startCaptureAnimation(mThumbnail); - } - - /** - * Cancels on-going animations - */ - public void cancelAnimations() { - mAnimationManager.cancelAnimations(); - } - public boolean collapseCameraControls() { boolean ret = false; mCameraControls.collapse(); @@ -1066,6 +1028,10 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.v(TAG, "surfaceChanged: width = " + width + ", height = " + height); + + RectF r = new RectF(mSurfaceView.getLeft(), mSurfaceView.getTop(), + mSurfaceView.getRight(), mSurfaceView.getBottom()); + onPreviewRectChanged(r); } @Override diff --git a/src/com/android/camera/ui/CaptureAnimationOverlay.java b/src/com/android/camera/ui/CaptureAnimationOverlay.java new file mode 100644 index 000000000..36c9a1131 --- /dev/null +++ b/src/com/android/camera/ui/CaptureAnimationOverlay.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2013 The Android Open Source 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.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +/** + * This class handles all the animations at capture time. Post capture animations + * will be handled in a separate place. + */ +public class CaptureAnimationOverlay extends View { + private final static String TAG = CaptureAnimationOverlay.class.getSimpleName(); + + private final static int FLASH_COLOR = Color.WHITE; + + private static final float FLASH_MAX_ALPHA = 0.85f; + private static final long FLASH_FULL_DURATION_MS = 65; + private static final long FLASH_DECREASE_DURATION_MS = 150; + private static final float SHORT_FLASH_MAX_ALPHA = 0.75f; + private static final long SHORT_FLASH_FULL_DURATION_MS = 34; + private static final long SHORT_FLASH_DECREASE_DURATION_MS = 100; + + private AnimatorSet mFlashAnimation; + private final RectF mPreviewArea = new RectF(); + private final Paint mPaint = new Paint(); + private final Interpolator mFlashAnimInterpolator; + private final ValueAnimator.AnimatorUpdateListener mFlashAnimUpdateListener; + private final Animator.AnimatorListener mFlashAnimListener; + + public CaptureAnimationOverlay(Context context, AttributeSet attrs) { + super(context, attrs); + mPaint.setColor(FLASH_COLOR); + mFlashAnimInterpolator = new LinearInterpolator(); + mFlashAnimUpdateListener = new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float alpha = 255.0f * (Float) animation.getAnimatedValue(); + mPaint.setAlpha((int) alpha); + invalidate(); + } + }; + mFlashAnimListener = new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + setVisibility(VISIBLE); + } + + @Override + public void onAnimationEnd(Animator animation) { + mFlashAnimation = null; + setVisibility(GONE); + } + + @Override + public void onAnimationCancel(Animator animation) { + // End is always called after cancel. + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }; + } + + /** + * Start flash animation. + * + * @param shortFlash show shortest possible flash instead of regular long version. + */ + public void startFlashAnimation(boolean shortFlash) { + if (mFlashAnimation != null && mFlashAnimation.isRunning()) { + mFlashAnimation.cancel(); + } + float maxAlpha; + + if (shortFlash) { + maxAlpha = SHORT_FLASH_MAX_ALPHA; + } else { + maxAlpha = FLASH_MAX_ALPHA; + } + + ValueAnimator flashAnim1 = ValueAnimator.ofFloat(maxAlpha, maxAlpha); + ValueAnimator flashAnim2 = ValueAnimator.ofFloat(maxAlpha, .0f); + + if (shortFlash) { + flashAnim1.setDuration(SHORT_FLASH_FULL_DURATION_MS); + flashAnim2.setDuration(SHORT_FLASH_DECREASE_DURATION_MS); + } else { + flashAnim1.setDuration(FLASH_FULL_DURATION_MS); + flashAnim2.setDuration(FLASH_DECREASE_DURATION_MS); + } + + flashAnim1.addUpdateListener(mFlashAnimUpdateListener); + flashAnim2.addUpdateListener(mFlashAnimUpdateListener); + flashAnim1.setInterpolator(mFlashAnimInterpolator); + flashAnim2.setInterpolator(mFlashAnimInterpolator); + + mFlashAnimation = new AnimatorSet(); + mFlashAnimation.play(flashAnim1).before(flashAnim2); + mFlashAnimation.addListener(mFlashAnimListener); + mFlashAnimation.start(); + } + + @Override + public void onDraw(Canvas canvas) { + if (mFlashAnimation != null && mFlashAnimation.isRunning()) { + // mPaint alpha is animated by the animation. + canvas.drawRect(mPreviewArea, mPaint); + canvas.clipRect(mPreviewArea); + } + } + + @Override + public boolean hasOverlappingRendering() { + // The internal draw method will NOT have draw calls that overlap. + return false; + } + + public void setPreviewRect(RectF previewArea) { + mPreviewArea.set(previewArea); + } +} -- cgit v1.2.3