| /* |
| * Copyright (C) 2014 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.widget; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorSet; |
| import android.animation.ValueAnimator; |
| import android.content.Context; |
| import android.graphics.Bitmap; |
| import android.graphics.Canvas; |
| import android.graphics.Point; |
| import android.graphics.Rect; |
| import android.graphics.drawable.BitmapDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.util.AttributeSet; |
| import android.view.animation.AccelerateInterpolator; |
| import android.view.animation.DecelerateInterpolator; |
| import android.widget.ImageView; |
| |
| import com.android.camera.util.CameraUtil; |
| import com.android.camera2.R; |
| |
| /** |
| * An ImageView which has the built-in peek animation support. |
| */ |
| public class PeekView extends ImageView { |
| |
| private static final float ROTATE_ANGLE = -7f; |
| private static final long PEEK_IN_DURATION_MS = 200; |
| private static final long PEEK_STAY_DURATION_MS = 100; |
| private static final long PEEK_OUT_DURATION_MS = 200; |
| private static final float FILMSTRIP_SCALE = 0.7f; |
| |
| private AnimatorSet mPeekAnimator; |
| private float mPeekRotateAngle; |
| private Point mRotationPivot; |
| private float mRotateScale; |
| private boolean mAnimationCanceled; |
| private Drawable mImageDrawable; |
| private Rect mDrawableBound; |
| |
| public PeekView(Context context) { |
| super(context); |
| init(); |
| } |
| |
| public PeekView(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| init(); |
| } |
| |
| public PeekView(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| init(); |
| } |
| |
| private void init() { |
| mRotationPivot = new Point(); |
| } |
| |
| @Override |
| protected void onDraw(Canvas c) { |
| super.onDraw(c); |
| if (mImageDrawable == null) { |
| return; |
| } |
| c.save(); |
| c.rotate(mPeekRotateAngle, mRotationPivot.x, mRotationPivot.y); |
| mImageDrawable.setBounds(mDrawableBound); |
| mImageDrawable.draw(c); |
| c.restore(); |
| } |
| |
| /** |
| * Starts the peek animation. |
| * |
| * @param bitmap The bitmap for the animation. |
| * @param strong {@code true} if the animation is the strong version which |
| * shows more portion of the bitmap. |
| * @param accessibilityString An accessibility String to be announced |
| during the peek animation. |
| */ |
| public void startPeekAnimation(final Bitmap bitmap, boolean strong, |
| String accessibilityString) { |
| ValueAnimator.AnimatorUpdateListener updateListener = |
| new ValueAnimator.AnimatorUpdateListener() { |
| @Override |
| public void onAnimationUpdate(ValueAnimator valueAnimator) { |
| mPeekRotateAngle = mRotateScale * (Float) valueAnimator.getAnimatedValue(); |
| invalidate(); |
| } |
| }; |
| ValueAnimator peekAnimateIn = ValueAnimator.ofFloat(0f, ROTATE_ANGLE); |
| ValueAnimator peekAnimateStay = ValueAnimator.ofFloat(ROTATE_ANGLE, ROTATE_ANGLE); |
| ValueAnimator peekAnimateOut = ValueAnimator.ofFloat(ROTATE_ANGLE, 0f); |
| peekAnimateIn.addUpdateListener(updateListener); |
| peekAnimateOut.addUpdateListener(updateListener); |
| peekAnimateIn.setDuration(PEEK_IN_DURATION_MS); |
| peekAnimateStay.setDuration(PEEK_STAY_DURATION_MS); |
| peekAnimateOut.setDuration(PEEK_OUT_DURATION_MS); |
| peekAnimateIn.setInterpolator(new DecelerateInterpolator()); |
| peekAnimateOut.setInterpolator(new AccelerateInterpolator()); |
| mPeekAnimator = new AnimatorSet(); |
| mPeekAnimator.playSequentially(peekAnimateIn, peekAnimateStay, peekAnimateOut); |
| mPeekAnimator.addListener(new Animator.AnimatorListener() { |
| @Override |
| public void onAnimationStart(Animator animator) { |
| setVisibility(VISIBLE); |
| mAnimationCanceled = false; |
| invalidate(); |
| } |
| |
| @Override |
| public void onAnimationEnd(Animator animator) { |
| if (!mAnimationCanceled) { |
| clear(); |
| } |
| } |
| |
| @Override |
| public void onAnimationCancel(Animator animator) { |
| mAnimationCanceled = true; |
| } |
| |
| @Override |
| public void onAnimationRepeat(Animator animator) { |
| |
| } |
| }); |
| |
| mRotateScale = (strong ? 1.0f : 0.5f); |
| mImageDrawable = new BitmapDrawable(getResources(), bitmap); |
| Point drawDim = CameraUtil.resizeToFill(mImageDrawable.getIntrinsicWidth(), |
| mImageDrawable.getIntrinsicHeight(), 0, (int) (getWidth() * FILMSTRIP_SCALE), |
| (int) (getHeight() * FILMSTRIP_SCALE)); |
| int x = getMeasuredWidth(); |
| int y = (getMeasuredHeight() - drawDim.y) / 2; |
| mDrawableBound = new Rect(x, y, x + drawDim.x, y + drawDim.y); |
| mRotationPivot.set(x, (int) (y + drawDim.y * 1.1)); |
| mPeekAnimator.start(); |
| |
| announceForAccessibility(accessibilityString); |
| } |
| |
| /** |
| * @return whether the animation is running. |
| */ |
| public boolean isPeekAnimationRunning() { |
| return mPeekAnimator.isRunning(); |
| } |
| |
| /** |
| * Stops the animation. See {@link android.animation.Animator#end()}. |
| */ |
| public void stopPeekAnimation() { |
| if (isPeekAnimationRunning()) { |
| mPeekAnimator.end(); |
| } else { |
| clear(); |
| } |
| } |
| |
| /** |
| * Cancels the animation. See {@link android.animation.Animator#cancel()}. |
| */ |
| public void cancelPeekAnimation() { |
| if (isPeekAnimationRunning()) { |
| mPeekAnimator.cancel(); |
| } else { |
| clear(); |
| } |
| } |
| |
| private void clear() { |
| setVisibility(INVISIBLE); |
| setImageDrawable(null); |
| } |
| } |