Home | History | Annotate | Download | only in widget
      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 
     56     public PeekView(Context context) {
     57         super(context);
     58         init();
     59     }
     60 
     61     public PeekView(Context context, AttributeSet attrs) {
     62         super(context, attrs);
     63         init();
     64     }
     65 
     66     public PeekView(Context context, AttributeSet attrs, int defStyle) {
     67         super(context, attrs, defStyle);
     68         init();
     69     }
     70 
     71     private void init() {
     72         mRotationPivot = new Point();
     73     }
     74 
     75     @Override
     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      */
     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      */
    162     public boolean isPeekAnimationRunning() {
    163         return mPeekAnimator.isRunning();
    164     }
    165 
    166     /**
    167      * Stops the animation. See {@link android.animation.Animator#end()}.
    168      */
    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      */
    180     public void cancelPeekAnimation() {
    181         if (isPeekAnimationRunning()) {
    182             mPeekAnimator.cancel();
    183         } else {
    184             clear();
    185         }
    186     }
    187 
    188     private void clear() {
    189         setVisibility(INVISIBLE);
    190         setImageDrawable(null);
    191     }
    192 }
    193