1/*
2 * Copyright (C) 2012 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 */
16package com.android.internal.policy.impl.keyguard;
17
18import android.animation.Animator;
19import android.animation.AnimatorListenerAdapter;
20import android.animation.ValueAnimator;
21import android.animation.ValueAnimator.AnimatorUpdateListener;
22import android.content.Context;
23import android.content.res.TypedArray;
24import android.graphics.Canvas;
25import android.graphics.drawable.Drawable;
26import android.util.AttributeSet;
27import android.view.animation.DecelerateInterpolator;
28import android.view.animation.Interpolator;
29import android.view.animation.LinearInterpolator;
30import android.widget.LinearLayout;
31
32import com.android.internal.R;
33
34/**
35 * A layout which animates a strip of horizontal, pulsing dots on request. This is used
36 * to indicate the presence of pages to the left / right.
37 */
38public class KeyguardGlowStripView extends LinearLayout {
39    private static final int DURATION = 500;
40
41    private static final float SLIDING_WINDOW_SIZE = 0.4f;
42    private int mDotStripTop;
43    private int mHorizontalDotGap;
44
45    private int mDotSize;
46    private int mNumDots;
47    private Drawable mDotDrawable;
48    private boolean mLeftToRight = true;
49
50    private float mAnimationProgress = 0f;
51    private boolean mDrawDots = false;
52    private ValueAnimator mAnimator;
53    private Interpolator mDotAlphaInterpolator = new DecelerateInterpolator(0.5f);
54
55    public KeyguardGlowStripView(Context context) {
56        this(context, null, 0);
57    }
58
59    public KeyguardGlowStripView(Context context, AttributeSet attrs) {
60        this(context, attrs, 0);
61    }
62
63    public KeyguardGlowStripView(Context context, AttributeSet attrs, int defStyle) {
64        super(context, attrs, defStyle);
65
66        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyguardGlowStripView);
67        mDotSize = a.getDimensionPixelSize(R.styleable.KeyguardGlowStripView_dotSize, mDotSize);
68        mNumDots = a.getInt(R.styleable.KeyguardGlowStripView_numDots, mNumDots);
69        mDotDrawable = a.getDrawable(R.styleable.KeyguardGlowStripView_glowDot);
70        mLeftToRight = a.getBoolean(R.styleable.KeyguardGlowStripView_leftToRight, mLeftToRight);
71    }
72
73    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
74        int availableWidth = w - getPaddingLeft() - getPaddingRight();
75        mHorizontalDotGap = (availableWidth - mDotSize * mNumDots) /  (mNumDots - 1);
76        mDotStripTop = getPaddingTop();
77        invalidate();
78    }
79
80    @Override
81    protected void dispatchDraw(Canvas canvas) {
82        super.dispatchDraw(canvas);
83
84        if (!mDrawDots) return;
85
86        int xOffset = getPaddingLeft();
87        mDotDrawable.setBounds(0, 0, mDotSize, mDotSize);
88
89        for (int i = 0; i < mNumDots; i++) {
90            // We fudge the relative position to provide a fade in of the first dot and a fade
91            // out of the final dot.
92            float relativeDotPosition = SLIDING_WINDOW_SIZE / 2 + ((1.0f * i) / (mNumDots - 1)) *
93                    (1 - SLIDING_WINDOW_SIZE);
94            float distance = Math.abs(relativeDotPosition - mAnimationProgress);
95            float alpha = Math.max(0, 1 - distance / (SLIDING_WINDOW_SIZE / 2));
96
97            alpha = mDotAlphaInterpolator.getInterpolation(alpha);
98
99            canvas.save();
100            canvas.translate(xOffset, mDotStripTop);
101            mDotDrawable.setAlpha((int) (alpha * 255));
102            mDotDrawable.draw(canvas);
103            canvas.restore();
104            xOffset += mDotSize + mHorizontalDotGap;
105        }
106    }
107
108    public void makeEmGo() {
109        if (mAnimator != null) {
110            mAnimator.cancel();
111        }
112        float from = mLeftToRight ? 0f : 1f;
113        float to = mLeftToRight ? 1f : 0f;
114        mAnimator = ValueAnimator.ofFloat(from, to);
115        mAnimator.setDuration(DURATION);
116        mAnimator.setInterpolator(new LinearInterpolator());
117        mAnimator.addListener(new AnimatorListenerAdapter() {
118            @Override
119            public void onAnimationEnd(Animator animation) {
120                mDrawDots = false;
121                // make sure we draw one frame at the end with everything gone.
122                invalidate();
123            }
124
125            @Override
126            public void onAnimationStart(Animator animation) {
127                mDrawDots = true;
128            }
129        });
130        mAnimator.addUpdateListener(new AnimatorUpdateListener() {
131            @Override
132            public void onAnimationUpdate(ValueAnimator animation) {
133                mAnimationProgress = (Float) animation.getAnimatedValue();
134                invalidate();
135            }
136        });
137        mAnimator.start();
138    }
139}
140