1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera.widget; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorSet; 21 import android.animation.ValueAnimator; 22 import android.content.Context; 23 import android.graphics.Bitmap; 24 import android.graphics.Canvas; 25 import android.graphics.Point; 26 import android.graphics.Rect; 27 import android.graphics.drawable.BitmapDrawable; 28 import android.graphics.drawable.Drawable; 29 import android.util.AttributeSet; 30 import android.view.animation.AccelerateInterpolator; 31 import android.view.animation.DecelerateInterpolator; 32 import android.widget.ImageView; 33 34 import com.android.camera.util.CameraUtil; 35 import com.android.camera2.R; 36 37 /** 38 * An ImageView which has the built-in peek animation support. 39 */ 40 public class PeekView extends ImageView { 41 42 private static final float ROTATE_ANGLE = -7f; 43 private static final long PEEK_IN_DURATION_MS = 200; 44 private static final long PEEK_STAY_DURATION_MS = 100; 45 private static final long PEEK_OUT_DURATION_MS = 200; 46 private static final float FILMSTRIP_SCALE = 0.7f; 47 48 private AnimatorSet mPeekAnimator; 49 private float mPeekRotateAngle; 50 private Point mRotationPivot; 51 private float mRotateScale; 52 private boolean mAnimationCanceled; 53 private Drawable mImageDrawable; 54 private Rect mDrawableBound; 55 PeekView(Context context)56 public PeekView(Context context) { 57 super(context); 58 init(); 59 } 60 PeekView(Context context, AttributeSet attrs)61 public PeekView(Context context, AttributeSet attrs) { 62 super(context, attrs); 63 init(); 64 } 65 PeekView(Context context, AttributeSet attrs, int defStyle)66 public PeekView(Context context, AttributeSet attrs, int defStyle) { 67 super(context, attrs, defStyle); 68 init(); 69 } 70 init()71 private void init() { 72 mRotationPivot = new Point(); 73 } 74 75 @Override onDraw(Canvas c)76 protected void onDraw(Canvas c) { 77 super.onDraw(c); 78 if (mImageDrawable == null) { 79 return; 80 } 81 c.save(); 82 c.rotate(mPeekRotateAngle, mRotationPivot.x, mRotationPivot.y); 83 mImageDrawable.setBounds(mDrawableBound); 84 mImageDrawable.draw(c); 85 c.restore(); 86 } 87 88 /** 89 * Starts the peek animation. 90 * 91 * @param bitmap The bitmap for the animation. 92 * @param strong {@code true} if the animation is the strong version which 93 * shows more portion of the bitmap. 94 * @param accessibilityString An accessibility String to be announced 95 during the peek animation. 96 */ startPeekAnimation(final Bitmap bitmap, boolean strong, String accessibilityString)97 public void startPeekAnimation(final Bitmap bitmap, boolean strong, 98 String accessibilityString) { 99 ValueAnimator.AnimatorUpdateListener updateListener = 100 new ValueAnimator.AnimatorUpdateListener() { 101 @Override 102 public void onAnimationUpdate(ValueAnimator valueAnimator) { 103 mPeekRotateAngle = mRotateScale * (Float) valueAnimator.getAnimatedValue(); 104 invalidate(); 105 } 106 }; 107 ValueAnimator peekAnimateIn = ValueAnimator.ofFloat(0f, ROTATE_ANGLE); 108 ValueAnimator peekAnimateStay = ValueAnimator.ofFloat(ROTATE_ANGLE, ROTATE_ANGLE); 109 ValueAnimator peekAnimateOut = ValueAnimator.ofFloat(ROTATE_ANGLE, 0f); 110 peekAnimateIn.addUpdateListener(updateListener); 111 peekAnimateOut.addUpdateListener(updateListener); 112 peekAnimateIn.setDuration(PEEK_IN_DURATION_MS); 113 peekAnimateStay.setDuration(PEEK_STAY_DURATION_MS); 114 peekAnimateOut.setDuration(PEEK_OUT_DURATION_MS); 115 peekAnimateIn.setInterpolator(new DecelerateInterpolator()); 116 peekAnimateOut.setInterpolator(new AccelerateInterpolator()); 117 mPeekAnimator = new AnimatorSet(); 118 mPeekAnimator.playSequentially(peekAnimateIn, peekAnimateStay, peekAnimateOut); 119 mPeekAnimator.addListener(new Animator.AnimatorListener() { 120 @Override 121 public void onAnimationStart(Animator animator) { 122 setVisibility(VISIBLE); 123 mAnimationCanceled = false; 124 invalidate(); 125 } 126 127 @Override 128 public void onAnimationEnd(Animator animator) { 129 if (!mAnimationCanceled) { 130 clear(); 131 } 132 } 133 134 @Override 135 public void onAnimationCancel(Animator animator) { 136 mAnimationCanceled = true; 137 } 138 139 @Override 140 public void onAnimationRepeat(Animator animator) { 141 142 } 143 }); 144 145 mRotateScale = (strong ? 1.0f : 0.5f); 146 mImageDrawable = new BitmapDrawable(getResources(), bitmap); 147 Point drawDim = CameraUtil.resizeToFill(mImageDrawable.getIntrinsicWidth(), 148 mImageDrawable.getIntrinsicHeight(), 0, (int) (getWidth() * FILMSTRIP_SCALE), 149 (int) (getHeight() * FILMSTRIP_SCALE)); 150 int x = getMeasuredWidth(); 151 int y = (getMeasuredHeight() - drawDim.y) / 2; 152 mDrawableBound = new Rect(x, y, x + drawDim.x, y + drawDim.y); 153 mRotationPivot.set(x, (int) (y + drawDim.y * 1.1)); 154 mPeekAnimator.start(); 155 156 announceForAccessibility(accessibilityString); 157 } 158 159 /** 160 * @return whether the animation is running. 161 */ isPeekAnimationRunning()162 public boolean isPeekAnimationRunning() { 163 return mPeekAnimator.isRunning(); 164 } 165 166 /** 167 * Stops the animation. See {@link android.animation.Animator#end()}. 168 */ stopPeekAnimation()169 public void stopPeekAnimation() { 170 if (isPeekAnimationRunning()) { 171 mPeekAnimator.end(); 172 } else { 173 clear(); 174 } 175 } 176 177 /** 178 * Cancels the animation. See {@link android.animation.Animator#cancel()}. 179 */ cancelPeekAnimation()180 public void cancelPeekAnimation() { 181 if (isPeekAnimationRunning()) { 182 mPeekAnimator.cancel(); 183 } else { 184 clear(); 185 } 186 } 187 clear()188 private void clear() { 189 setVisibility(INVISIBLE); 190 setImageDrawable(null); 191 } 192 } 193