PeekView.java revision 75fc98f248f4383e3dc10048bfdf0292fd8dd970
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
17package com.android.camera.widget;
18
19import android.animation.Animator;
20import android.animation.AnimatorSet;
21import android.animation.ValueAnimator;
22import android.content.Context;
23import android.graphics.Bitmap;
24import android.graphics.Canvas;
25import android.graphics.Point;
26import android.graphics.Rect;
27import android.graphics.drawable.BitmapDrawable;
28import android.graphics.drawable.Drawable;
29import android.util.AttributeSet;
30import android.view.animation.AccelerateInterpolator;
31import android.view.animation.DecelerateInterpolator;
32import android.widget.ImageView;
33
34import com.android.camera.util.CameraUtil;
35import com.android.camera2.R;
36
37/**
38 * An ImageView which has the built-in peek animation support.
39 */
40public 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     */
95    public void startPeekAnimation(final Bitmap bitmap, boolean strong) {
96        ValueAnimator.AnimatorUpdateListener updateListener =
97                new ValueAnimator.AnimatorUpdateListener() {
98                    @Override
99                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
100                        mPeekRotateAngle = mRotateScale * (Float) valueAnimator.getAnimatedValue();
101                        invalidate();
102                    }
103                };
104        ValueAnimator peekAnimateIn = ValueAnimator.ofFloat(0f, ROTATE_ANGLE);
105        ValueAnimator peekAnimateStay = ValueAnimator.ofFloat(ROTATE_ANGLE, ROTATE_ANGLE);
106        ValueAnimator peekAnimateOut = ValueAnimator.ofFloat(ROTATE_ANGLE, 0f);
107        peekAnimateIn.addUpdateListener(updateListener);
108        peekAnimateOut.addUpdateListener(updateListener);
109        peekAnimateIn.setDuration(PEEK_IN_DURATION_MS);
110        peekAnimateStay.setDuration(PEEK_STAY_DURATION_MS);
111        peekAnimateOut.setDuration(PEEK_OUT_DURATION_MS);
112        peekAnimateIn.setInterpolator(new DecelerateInterpolator());
113        peekAnimateOut.setInterpolator(new AccelerateInterpolator());
114        mPeekAnimator = new AnimatorSet();
115        mPeekAnimator.playSequentially(peekAnimateIn, peekAnimateStay, peekAnimateOut);
116        mPeekAnimator.addListener(new Animator.AnimatorListener() {
117            @Override
118            public void onAnimationStart(Animator animator) {
119                setVisibility(VISIBLE);
120                mAnimationCanceled = false;
121                invalidate();
122            }
123
124            @Override
125            public void onAnimationEnd(Animator animator) {
126                if (!mAnimationCanceled) {
127                    clear();
128                }
129            }
130
131            @Override
132            public void onAnimationCancel(Animator animator) {
133                mAnimationCanceled = true;
134            }
135
136            @Override
137            public void onAnimationRepeat(Animator animator) {
138
139            }
140        });
141
142        mRotateScale = (strong ? 1.0f : 0.5f);
143        mImageDrawable = new BitmapDrawable(getResources(), bitmap);
144        Point drawDim = CameraUtil.resizeToFill(mImageDrawable.getIntrinsicWidth(),
145                mImageDrawable.getIntrinsicHeight(), 0, (int) (getWidth() * FILMSTRIP_SCALE),
146                (int) (getHeight() * FILMSTRIP_SCALE));
147        int x = getMeasuredWidth();
148        int y = (getMeasuredHeight() - drawDim.y) / 2;
149        mDrawableBound = new Rect(x, y, x + drawDim.x, y + drawDim.y);
150        mRotationPivot.set(x, (int) (y + drawDim.y * 1.1));
151        mPeekAnimator.start();
152        announceForAccessibility(
153                getContext().getResources().getString(R.string.accessibility_peek));
154    }
155
156    /**
157     * @return whether the animation is running.
158     */
159    public boolean isPeekAnimationRunning() {
160        return mPeekAnimator.isRunning();
161    }
162
163    /**
164     * Stops the animation. See {@link android.animation.Animator#end()}.
165     */
166    public void stopPeekAnimation() {
167        if (isPeekAnimationRunning()) {
168            mPeekAnimator.end();
169        } else {
170            clear();
171        }
172    }
173
174    /**
175     * Cancels the animation. See {@link android.animation.Animator#cancel()}.
176     */
177    public void cancelPeekAnimation() {
178        if (isPeekAnimationRunning()) {
179            mPeekAnimator.cancel();
180        } else {
181            clear();
182        }
183    }
184
185    private void clear() {
186        setVisibility(INVISIBLE);
187        setImageDrawable(null);
188    }
189}
190