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