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

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

Issue 2779543005: Add support for highlighting menu items (Closed)
Patch Set: Debugged drawable issues 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/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);
+ }
+ };
+}

Powered by Google App Engine
This is Rietveld 408576698