OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.chrome.browser.appmenu; | |
6 | |
7 import android.content.res.Resources; | |
8 import android.graphics.Canvas; | |
9 import android.graphics.ColorFilter; | |
10 import android.graphics.Paint; | |
11 import android.graphics.PixelFormat; | |
12 import android.graphics.Rect; | |
13 import android.graphics.drawable.Drawable; | |
14 import android.os.SystemClock; | |
15 import android.support.annotation.ColorInt; | |
16 import android.support.annotation.ColorRes; | |
17 import android.view.animation.Interpolator; | |
18 | |
19 import org.chromium.base.ApiCompatibilityUtils; | |
20 | |
21 /** | |
22 * A custom {@link Drawable} that will animate a pulse using the {@link PulseInt erpolator}. Meant | |
23 * to be created with a {@link PulseDrawable#Painter} that does the actual drawi ng work based on | |
24 * the pulse interpolation value. | |
25 */ | |
26 public class PulseDrawable extends Drawable { | |
David Trainor- moved to gerrit
2017/04/11 18:43:04
These should be in a better, more generic place.
| |
27 private static final long PULSE_DURATION_MS = 2500; | |
28 private static final long FRAME_RATE = 60; | |
29 | |
30 /** | |
31 * An interface that does the actual drawing work for this {@link Drawable}. Not meant to be | |
32 * stateful, as this could be shared across multiple instances of this drawa ble if it gets | |
33 * copied or mutated. | |
34 */ | |
35 public interface Painter { | |
36 /** | |
37 * Called when this drawable updates it's pulse interpolation. Should m utate the drawable | |
38 * as necessary. This is responsible for invalidating this {@link Drawa ble} if something | |
39 * needs to be redrawn. | |
40 * | |
41 * @param drawable The {@link PulseDrawable} that is updated. | |
42 * @param interpolation The current progress of whatever is being pulsed . | |
43 */ | |
44 void modifyDrawable(PulseDrawable drawable, float interpolation); | |
45 | |
46 /** | |
47 * Called when this {@link PulseDrawable} needs to draw. Should perform any draw operation | |
48 * for the specific type of pulse. | |
49 * @param drawable The calling {@link PulseDrawable}. | |
50 * @param paint A {@link Paint} object to use. This will automa tically have the | |
51 * color set. | |
52 * @param canvas The {@link Canvas} to draw to. | |
53 * @param interpolation The current progress of whatever is being pulsed . | |
54 */ | |
55 void draw(PulseDrawable drawable, Paint paint, Canvas canvas, float inte rpolation); | |
56 } | |
57 | |
58 private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); | |
59 private final Rect mInset = new Rect(); | |
60 private final Rect mOriginalBounds = new Rect(); | |
61 private final Rect mInsetBounds = new Rect(); | |
62 | |
63 protected PulseState mState; | |
64 private boolean mMutated; | |
65 | |
66 /** | |
67 * Creates a new {@link PulseDrawable} instance. | |
68 * @param res A {@link Resources} object used to load the {@link Dr awable} parameters. | |
69 * @param color A color resource. | |
70 * @param interpolator An {@link Interpolator} that defines how the pulse wi ll fade in and out. | |
71 * @param painter The {@link Painter} that will be responsible for draw ing the pulse. | |
72 */ | |
73 public PulseDrawable( | |
74 Resources res, @ColorRes int color, Interpolator interpolator, Paint er painter) { | |
75 this(new PulseState(res, color, interpolator, painter)); | |
76 } | |
77 | |
78 private PulseDrawable(PulseState state) { | |
79 mState = state; | |
80 } | |
81 | |
82 /** What color to set the pulse to. */ | |
83 public void setColor(@ColorInt int color) { | |
84 mState.color = mState.drawColor = color; | |
85 invalidateSelf(); | |
86 } | |
87 | |
88 /** How much to inset the bounds of this {@link Drawable} by. */ | |
89 public void setInset(int left, int top, int right, int bottom) { | |
90 mInset.set(left, top, right, bottom); | |
91 setBounds(mOriginalBounds); | |
92 } | |
93 | |
94 // Drawable implementation. | |
95 // Overriding only this method because {@link Drawable#setBounds(Rect)} call s into this. | |
96 @Override | |
97 public void setBounds(int left, int top, int right, int bottom) { | |
98 mOriginalBounds.set(left, top, right, bottom); | |
99 mInsetBounds.set( | |
100 left + mInset.left, top + mInset.top, right - mInset.right, bott om - mInset.bottom); | |
101 super.setBounds( | |
102 mInsetBounds.left, mInsetBounds.top, mInsetBounds.right, mInsetB ounds.bottom); | |
103 } | |
104 | |
105 @Override | |
106 public void draw(Canvas canvas) { | |
107 mPaint.setColor(mState.drawColor); | |
108 mState.painter.draw(this, mPaint, canvas, mState.progress); | |
109 } | |
110 | |
111 @Override | |
112 public void setAlpha(int alpha) { | |
113 // Encode the alpha into the color. | |
114 alpha += alpha >> 7; // make it 0..256 | |
115 final int baseAlpha = mState.color >>> 24; | |
116 final int useAlpha = baseAlpha * alpha >> 8; | |
117 final int useColor = (mState.color << 8 >>> 8) | (useAlpha << 24); | |
118 if (mState.drawColor != useColor) { | |
119 mState.drawColor = useColor; | |
120 invalidateSelf(); | |
121 } | |
122 } | |
123 | |
124 @Override | |
125 public int getAlpha() { | |
126 return mState.drawColor >>> 24; | |
127 } | |
128 | |
129 @Override | |
130 public void setColorFilter(ColorFilter colorFilter) { | |
131 mPaint.setColorFilter(colorFilter); | |
132 } | |
133 | |
134 @Override | |
135 public int getOpacity() { | |
136 return PixelFormat.TRANSLUCENT; | |
137 } | |
138 | |
139 @Override | |
140 public boolean setVisible(boolean visible, boolean restart) { | |
141 final boolean changed = super.setVisible(visible, restart); | |
142 if (visible) { | |
143 if (changed || restart) { | |
144 startPulsing(); | |
145 } | |
146 } else { | |
147 stopPulsing(); | |
148 } | |
149 return changed; | |
150 } | |
151 | |
152 @Override | |
153 public Drawable mutate() { | |
154 if (!mMutated && super.mutate() == this) { | |
155 mState = new PulseState(mState); | |
156 mMutated = true; | |
157 } | |
158 return this; | |
159 } | |
160 | |
161 private void startPulsing() { | |
162 mState.startTime = SystemClock.uptimeMillis(); | |
163 mNextFrame.run(); | |
164 } | |
165 | |
166 private void stopPulsing() { | |
167 unscheduleSelf(mNextFrame); | |
168 } | |
169 | |
170 /** | |
171 * The {@link ConstantState} subclass for this {@link PulseDrawable}. | |
172 */ | |
173 private static final class PulseState extends ConstantState { | |
174 // Current Paint State. | |
175 /** The current color, including alpha, to draw. */ | |
176 public int drawColor; | |
177 | |
178 /** The original color to draw (may not include any alpha updates. */ | |
179 public int color; | |
180 | |
181 // Current Animation State | |
182 /** The time from {@link SystemClock#updateMillis()} that this animation started at. */ | |
183 public long startTime; | |
184 | |
185 /** The current progress from 0 to 1 of the pulse. */ | |
186 public float progress; | |
187 | |
188 /** The {@link Interpolator} that makes the pulse and generates the prog ress. */ | |
189 public Interpolator interpolator; | |
190 | |
191 /** | |
192 * The {@link Painter} object that is responsible for modifying and draw ing this | |
193 * {@link PulseDrawable}. | |
194 */ | |
195 public Painter painter; | |
196 | |
197 PulseState(Resources res, @ColorRes int color, Interpolator interpolator , Painter painter) { | |
198 this.color = this.drawColor = ApiCompatibilityUtils.getColor(res, co lor); | |
199 | |
200 this.interpolator = new PulseInterpolator(interpolator); | |
201 this.painter = painter; | |
202 } | |
203 | |
204 PulseState(PulseState other) { | |
205 drawColor = other.drawColor; | |
206 color = other.color; | |
207 | |
208 startTime = other.startTime; | |
209 | |
210 interpolator = other.interpolator; | |
211 painter = other.painter; | |
212 } | |
213 | |
214 @Override | |
215 public Drawable newDrawable() { | |
216 return new PulseDrawable(this); | |
217 } | |
218 | |
219 @Override | |
220 public int getChangingConfigurations() { | |
221 return 0; | |
222 } | |
223 } | |
224 | |
225 private final Runnable mNextFrame = new Runnable() { | |
226 @Override | |
227 public void run() { | |
228 long curTime = SystemClock.uptimeMillis(); | |
229 long msIntoAnim = (curTime - mState.startTime) % PULSE_DURATION_MS; | |
230 float progress = ((float) msIntoAnim) / ((float) PULSE_DURATION_MS); | |
231 mState.progress = mState.interpolator.getInterpolation(progress); | |
232 mState.painter.modifyDrawable(PulseDrawable.this, mState.progress); | |
233 | |
234 unscheduleSelf(mNextFrame); | |
235 scheduleSelf(mNextFrame, SystemClock.uptimeMillis() + 1000 / FRAME_R ATE); | |
236 } | |
237 }; | |
238 } | |
OLD | NEW |