Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2056)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java

Issue 2779543005: Add support for highlighting menu items (Closed)
Patch Set: Moved PulseDrawable Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b2aeaafe6d867f10cdd9b5dda91ca9da57ee5bc
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/PulseDrawable.java
@@ -0,0 +1,318 @@
+// 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.widget;
+
+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.v4.view.animation.FastOutSlowInInterpolator;
+import android.support.v4.view.animation.PathInterpolatorCompat;
+import android.view.animation.Interpolator;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.ContextUtils;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.util.MathUtils;
+
+/**
+ * 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.
+ */
+ private 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);
+ }
+
+ /**
+ * Creates a {@link PulseDrawable} that will fill the bounds with a pulsing color.
+ * @return A new {@link PulseDrawable} instance.
+ */
+ public static PulseDrawable createHighlight() {
+ PulseDrawable.Painter painter = new PulseDrawable.Painter() {
+ @Override
+ public void modifyDrawable(PulseDrawable drawable, float interpolation) {
+ drawable.setAlpha((int) MathUtils.interpolate(12, 75, interpolation));
+ }
+
+ @Override
+ public void draw(
+ PulseDrawable drawable, Paint paint, Canvas canvas, float interpolation) {
+ canvas.drawRect(drawable.getBounds(), paint);
+ }
+ };
+
+ return new PulseDrawable(new FastOutSlowInInterpolator(), painter);
+ }
+
+ /**
+ * Creates a {@link PulseDrawable} that will draw a pulsing circle inside the bounds.
+ * @return A new {@link PulseDrawable} instance.
+ */
+ public static PulseDrawable createCircle() {
+ PulseDrawable.Painter painter = new PulseDrawable.Painter() {
+ @Override
+ public void modifyDrawable(PulseDrawable drawable, float interpolation) {
+ drawable.invalidateSelf();
+ }
+
+ @Override
+ public void draw(
+ PulseDrawable drawable, Paint paint, Canvas canvas, float interpolation) {
+ Rect bounds = drawable.getBounds();
+ float scale = MathUtils.interpolate(0.8f, 1.f, interpolation);
+ float radius = Math.min(bounds.width(), bounds.height()) * scale / 2.f;
+ canvas.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), radius, paint);
+ }
+ };
+
+ PulseDrawable drawable =
+ new PulseDrawable(PathInterpolatorCompat.create(.8f, 0.f, .6f, 1.f), painter);
+ drawable.setAlpha(76);
+ return drawable;
+ }
+
+ private final Runnable mNextFrame = new Runnable() {
+ @Override
+ public void run() {
+ stepPulse();
+ if (mRunning) scheduleSelf(mNextFrame, SystemClock.uptimeMillis() + 1000 / FRAME_RATE);
+ }
+ };
+
+ 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();
+
+ private PulseState mState;
+ private boolean mMutated;
+ private boolean mRunning;
+
+ /**
+ * Creates a new {@link PulseDrawable} instance.
+ * @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.
+ */
+ private PulseDrawable(Interpolator interpolator, Painter painter) {
+ this(new PulseState(interpolator, painter));
+ setUseLightPulseColor(false);
+ }
+
+ private PulseDrawable(PulseState state) {
+ mState = state;
+ }
+
+ /** Whether or not to use a light or dark color for the pulse. */
+ public void setUseLightPulseColor(boolean useLightPulseColor) {
+ Resources resources = ContextUtils.getApplicationContext().getResources();
+
+ @ColorInt
+ int color = ApiCompatibilityUtils.getColor(
+ resources, useLightPulseColor ? R.color.google_grey_100 : R.color.google_blue_500);
+ if (mState.color == color) return;
+
+ 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);
+ if (!mOriginalBounds.isEmpty()) setBounds(mOriginalBounds);
+ }
+
+ // 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 (will not include updates from calls to setAlpha()). */
+ 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(Interpolator interpolator, Painter painter) {
+ 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;
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698