Index: chrome/android/java/src/org/chromium/chrome/browser/appmenu/PulseDrawable.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/PulseDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/PulseDrawable.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b9cac118bbff001326e4c1096c16e37052d94c48 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/PulseDrawable.java |
@@ -0,0 +1,263 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.appmenu; |
+ |
+import android.content.res.Resources; |
+import android.graphics.Canvas; |
+import android.graphics.ColorFilter; |
+import android.graphics.Paint; |
+import android.graphics.PixelFormat; |
+import android.graphics.Rect; |
+import android.graphics.drawable.Animatable; |
+import android.graphics.drawable.Drawable; |
+import android.os.SystemClock; |
+import android.support.annotation.ColorInt; |
+import android.support.annotation.ColorRes; |
+import android.view.animation.Interpolator; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+ |
+/** |
+ * A custom {@link Drawable} that will animate a pulse using the {@link PulseInterpolator}. Meant |
+ * to be created with a {@link PulseDrawable#Painter} that does the actual drawing work based on |
+ * the pulse interpolation value. |
+ */ |
+public class PulseDrawable extends Drawable implements Animatable { |
+ private static final long PULSE_DURATION_MS = 2500; |
+ private static final long FRAME_RATE = 60; |
+ |
+ /** |
+ * An interface that does the actual drawing work for this {@link Drawable}. Not meant to be |
+ * stateful, as this could be shared across multiple instances of this drawable if it gets |
+ * copied or mutated. |
+ */ |
+ public interface Painter { |
+ /** |
+ * Called when this drawable updates it's pulse interpolation. Should mutate the drawable |
+ * as necessary. This is responsible for invalidating this {@link Drawable} if something |
+ * needs to be redrawn. |
+ * |
+ * @param drawable The {@link PulseDrawable} that is updated. |
+ * @param interpolation The current progress of whatever is being pulsed. |
+ */ |
+ void modifyDrawable(PulseDrawable drawable, float interpolation); |
+ |
+ /** |
+ * Called when this {@link PulseDrawable} needs to draw. Should perform any draw operation |
+ * for the specific type of pulse. |
+ * @param drawable The calling {@link PulseDrawable}. |
+ * @param paint A {@link Paint} object to use. This will automatically have the |
+ * color set. |
+ * @param canvas The {@link Canvas} to draw to. |
+ * @param interpolation The current progress of whatever is being pulsed. |
+ */ |
+ void draw(PulseDrawable drawable, Paint paint, Canvas canvas, float interpolation); |
+ } |
+ |
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); |
+ private final Rect mInset = new Rect(); |
+ private final Rect mOriginalBounds = new Rect(); |
+ private final Rect mInsetBounds = new Rect(); |
+ |
+ protected PulseState mState; |
Ted C
2017/04/12 18:05:57
why protected?
David Trainor- moved to gerrit
2017/04/12 18:59:37
Ah I was planning on overriding this class for the
|
+ private boolean mMutated; |
+ private boolean mRunning; |
+ |
+ /** |
+ * Creates a new {@link PulseDrawable} instance. |
+ * @param res A {@link Resources} object used to load the {@link Drawable} parameters. |
+ * @param color A color resource. |
+ * @param interpolator An {@link Interpolator} that defines how the pulse will fade in and out. |
+ * @param painter The {@link Painter} that will be responsible for drawing the pulse. |
+ */ |
+ public PulseDrawable( |
+ Resources res, @ColorRes int color, Interpolator interpolator, Painter painter) { |
+ this(new PulseState(res, color, interpolator, painter)); |
+ } |
+ |
+ private PulseDrawable(PulseState state) { |
+ mState = state; |
+ } |
+ |
+ /** What color to set the pulse to. */ |
+ public void setColor(@ColorInt int color) { |
Ted C
2017/04/12 18:05:57
I'd rather this not be configurable right now. I'
David Trainor- moved to gerrit
2017/04/12 18:59:37
I can have it set light/dark if you'd like. We on
|
+ int alpha = getAlpha(); |
+ mState.color = mState.drawColor = color; |
+ setAlpha(alpha); |
+ invalidateSelf(); |
+ } |
+ |
+ /** How much to inset the bounds of this {@link Drawable} by. */ |
+ public void setInset(int left, int top, int right, int bottom) { |
+ mInset.set(left, top, right, bottom); |
+ setBounds(mOriginalBounds); |
Ted C
2017/04/12 18:05:57
Should we only call this if !mOriginalBounds.isEmp
David Trainor- moved to gerrit
2017/04/12 18:59:37
Yes good point.
|
+ } |
+ |
+ // Animatable implementation. |
+ @Override |
+ public void start() { |
+ if (mRunning) { |
+ unscheduleSelf(mNextFrame); |
+ scheduleSelf(mNextFrame, SystemClock.uptimeMillis() + 1000 / FRAME_RATE); |
+ } else { |
+ mRunning = true; |
+ if (mState.startTime == 0) mState.startTime = SystemClock.uptimeMillis(); |
+ mNextFrame.run(); |
+ } |
+ } |
+ |
+ @Override |
+ public void stop() { |
+ mRunning = false; |
+ mState.startTime = 0; |
+ unscheduleSelf(mNextFrame); |
+ } |
+ |
+ @Override |
+ public boolean isRunning() { |
+ return mRunning; |
+ } |
+ |
+ // Drawable implementation. |
+ // Overriding only this method because {@link Drawable#setBounds(Rect)} calls into this. |
+ @Override |
+ public void setBounds(int left, int top, int right, int bottom) { |
+ mOriginalBounds.set(left, top, right, bottom); |
+ mInsetBounds.set( |
+ left + mInset.left, top + mInset.top, right - mInset.right, bottom - mInset.bottom); |
+ super.setBounds( |
+ mInsetBounds.left, mInsetBounds.top, mInsetBounds.right, mInsetBounds.bottom); |
+ } |
+ |
+ @Override |
+ public void draw(Canvas canvas) { |
+ mPaint.setColor(mState.drawColor); |
+ mState.painter.draw(this, mPaint, canvas, mState.progress); |
+ } |
+ |
+ @Override |
+ public void setAlpha(int alpha) { |
+ // Encode the alpha into the color. |
+ alpha += alpha >> 7; // make it 0..256 |
+ final int baseAlpha = mState.color >>> 24; |
+ final int useAlpha = baseAlpha * alpha >> 8; |
+ final int useColor = (mState.color << 8 >>> 8) | (useAlpha << 24); |
+ if (mState.drawColor != useColor) { |
+ mState.drawColor = useColor; |
+ invalidateSelf(); |
+ } |
+ } |
+ |
+ @Override |
+ public int getAlpha() { |
+ return mState.drawColor >>> 24; |
+ } |
+ |
+ @Override |
+ public void setColorFilter(ColorFilter colorFilter) { |
+ mPaint.setColorFilter(colorFilter); |
+ } |
+ |
+ @Override |
+ public int getOpacity() { |
+ return PixelFormat.TRANSLUCENT; |
+ } |
+ |
+ @Override |
+ public boolean setVisible(boolean visible, boolean restart) { |
+ final boolean changed = super.setVisible(visible, restart); |
+ if (visible) { |
+ if (changed || restart) start(); |
+ } else { |
+ stop(); |
+ } |
+ return changed; |
+ } |
+ |
+ @Override |
+ public Drawable mutate() { |
+ if (!mMutated && super.mutate() == this) { |
+ mState = new PulseState(mState); |
+ mMutated = true; |
+ } |
+ return this; |
+ } |
+ |
+ @Override |
+ public ConstantState getConstantState() { |
+ return mState; |
+ } |
+ |
+ private void stepPulse() { |
+ long curTime = SystemClock.uptimeMillis(); |
+ long msIntoAnim = (curTime - mState.startTime) % PULSE_DURATION_MS; |
+ float progress = ((float) msIntoAnim) / ((float) PULSE_DURATION_MS); |
+ mState.progress = mState.interpolator.getInterpolation(progress); |
+ mState.painter.modifyDrawable(PulseDrawable.this, mState.progress); |
+ } |
+ |
+ /** |
+ * The {@link ConstantState} subclass for this {@link PulseDrawable}. |
+ */ |
+ private static final class PulseState extends ConstantState { |
+ // Current Paint State. |
+ /** The current color, including alpha, to draw. */ |
+ public int drawColor; |
+ |
+ /** The original color to draw (may not include any alpha updates. */ |
Ted C
2017/04/12 18:05:57
may not, or does not? also missing trailing )
I
David Trainor- moved to gerrit
2017/04/12 18:59:37
It depends on whether or not the color passed in h
|
+ public int color; |
+ |
+ // Current Animation State |
+ /** The time from {@link SystemClock#updateMillis()} that this animation started at. */ |
+ public long startTime; |
+ |
+ /** The current progress from 0 to 1 of the pulse. */ |
+ public float progress; |
+ |
+ /** The {@link Interpolator} that makes the pulse and generates the progress. */ |
+ public Interpolator interpolator; |
+ |
+ /** |
+ * The {@link Painter} object that is responsible for modifying and drawing this |
+ * {@link PulseDrawable}. |
+ */ |
+ public Painter painter; |
+ |
+ PulseState(Resources res, @ColorRes int color, Interpolator interpolator, Painter painter) { |
+ this.color = this.drawColor = ApiCompatibilityUtils.getColor(res, color); |
+ |
+ this.interpolator = new PulseInterpolator(interpolator); |
+ this.painter = painter; |
+ } |
+ |
+ PulseState(PulseState other) { |
+ drawColor = other.drawColor; |
+ color = other.color; |
+ |
+ startTime = other.startTime; |
+ |
+ interpolator = other.interpolator; |
+ painter = other.painter; |
+ } |
+ |
+ @Override |
+ public Drawable newDrawable() { |
+ return new PulseDrawable(this); |
+ } |
+ |
+ @Override |
+ public int getChangingConfigurations() { |
+ return 0; |
+ } |
+ } |
+ |
+ private final Runnable mNextFrame = new Runnable() { |
Ted C
2017/04/12 18:05:57
I'd put this above the constructor as it is a loca
David Trainor- moved to gerrit
2017/04/12 18:59:37
Done.
|
+ @Override |
+ public void run() { |
+ stepPulse(); |
+ if (mRunning) scheduleSelf(mNextFrame, SystemClock.uptimeMillis() + 1000 / FRAME_RATE); |
+ } |
+ }; |
+} |