Skip to content

Commit

Permalink
[ProgressIndicator] Added ramping animation to wave amplitude for bot…
Browse files Browse the repository at this point in the history
…h Linear and Circular types. (roll forward)

PiperOrigin-RevId: 621631230
  • Loading branch information
pekingme authored and paulfthomas committed Apr 3, 2024
1 parent 0849c14 commit 23bcd50
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 6 deletions.
Expand Up @@ -180,6 +180,7 @@ void fillIndicator(
color,
activeIndicator.gapSize,
activeIndicator.gapSize,
activeIndicator.amplitudeFraction,
activeIndicator.phaseFraction,
/* shouldDrawActiveIndicator= */ true);
}
Expand All @@ -202,6 +203,7 @@ void fillTrack(
color,
gapSize,
gapSize,
/* amplitudeFraction= */ 0f,
/* phaseFraction= */ 0f,
/* shouldDrawActiveIndicator= */ false);
}
Expand All @@ -217,6 +219,7 @@ void fillTrack(
* @param paintColor The color used to draw the indicator.
* @param startGapSize The gap size applied to the start (rotating behind) of the drawing part.
* @param endGapSize The gap size applied to the end (rotating ahead) of the drawing part.
* @param amplitudeFraction The fraction [0, 1] of amplitude applied to the part.
* @param phaseFraction The fraction [0, 1] of initial phase in one cycle.
* @param shouldDrawActiveIndicator Whether this part should be drawn as an active indicator.
*/
Expand All @@ -228,6 +231,7 @@ private void drawArc(
@ColorInt int paintColor,
@Px int startGapSize,
@Px int endGapSize,
float amplitudeFraction,
float phaseFraction,
boolean shouldDrawActiveIndicator) {
float arcFraction =
Expand All @@ -249,6 +253,7 @@ private void drawArc(
paintColor,
startGapSize,
/* endGapSize= */ 0,
amplitudeFraction,
phaseFraction,
shouldDrawActiveIndicator);
drawArc(
Expand All @@ -259,6 +264,7 @@ private void drawArc(
paintColor,
/* startGapSize= */ 0,
endGapSize,
amplitudeFraction,
phaseFraction,
shouldDrawActiveIndicator);
return;
Expand Down Expand Up @@ -295,7 +301,8 @@ private void drawArc(
return;
}

boolean shouldDrawWavyPath = spec.hasWavyEffect() && shouldDrawActiveIndicator;
boolean shouldDrawWavyPath =
spec.hasWavyEffect() && shouldDrawActiveIndicator && amplitudeFraction > 0f;

// Sets up the paint.
paint.setAntiAlias(true);
Expand Down Expand Up @@ -343,6 +350,7 @@ private void drawArc(
displayedActivePath,
startDegreeWithoutCorners / 360,
arcDegreeWithoutCorners / 360,
amplitudeFraction,
phaseFraction);
canvas.drawPath(displayedActivePath, paint);
}
Expand Down Expand Up @@ -433,7 +441,7 @@ void invalidateCachedPaths() {
cachedActivePath.transform(transform);
if (spec.hasWavyEffect()) {
activePathMeasure.setPath(cachedActivePath, false);
createWavyPath(activePathMeasure, cachedActivePath, displayedAmplitude);
createWavyPath(activePathMeasure, cachedActivePath, cachedAmplitude);
}
activePathMeasure.setPath(cachedActivePath, false);
}
Expand Down Expand Up @@ -496,10 +504,12 @@ private Pair<PathPoint, PathPoint> getDisplayedPath(
@NonNull Path displayedPath,
float start,
float span,
float amplitudeFraction,
float phaseFraction) {
if (adjustedRadius != cachedRadius
|| (pathMeasure == activePathMeasure && displayedAmplitude != cachedAmplitude)) {
cachedAmplitude = displayedAmplitude;
|| (pathMeasure == activePathMeasure
&& displayedAmplitude * amplitudeFraction != cachedAmplitude)) {
cachedAmplitude = displayedAmplitude * amplitudeFraction;
cachedRadius = adjustedRadius;
invalidateCachedPaths();
}
Expand Down
Expand Up @@ -16,6 +16,9 @@

package com.google.android.material.progressindicator;

import com.google.android.material.R;

import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
Expand All @@ -28,20 +31,28 @@
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import com.google.android.material.animation.AnimationUtils;
import com.google.android.material.motion.MotionUtils;
import com.google.android.material.progressindicator.DrawingDelegate.ActiveIndicator;

/** This class draws the graphics for determinate mode. */
public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
extends DrawableWithAnimatedVisibilityChange {
// Constants for drawing progress.
private static final int MAX_DRAWABLE_LEVEL = 10000;
// Constants for amplitude animation.
private static final float FULL_AMPLITUDE_FRACTION_MIN = 0.1f;
private static final float FULL_AMPLITUDE_FRACTION_MAX = 0.9f;

// The constant for spring force stiffness.
private static final float SPRING_FORCE_STIFFNESS = SpringForce.STIFFNESS_VERY_LOW;
// If the progress is less than 1%, the gap will be proportional to the progress. So that, it
// draws a full track at 0%.
static final float GAP_RAMP_DOWN_THRESHOLD = 0.01f;
// The duration of repeated initial phase animation in ms. It can be any positive values.
private static final int PHASE_ANIMATION_DURATION_MS = 1000;
// The duration of amplitude ramping animation in ms.
private static final int AMPLITUDE_ANIMATION_DURATION_MS = 500;

// Drawing delegate object.
private DrawingDelegate<S> drawingDelegate;
Expand All @@ -51,10 +62,16 @@ public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
private final SpringAnimation springAnimation;
// Active indicator for the progress.
private final ActiveIndicator activeIndicator;
// Fraction of displayed amplitude.
private float targetAmplitudeFraction;
// Whether to skip the spring animation on level change event.
private boolean skipAnimationOnLevelChange = false;

@NonNull private final ValueAnimator phaseAnimator;
@NonNull private final ValueAnimator amplitudeAnimator;
private TimeInterpolator amplitudeInterpolator;
@NonNull private final TimeInterpolator amplitudeOnInterpolator;
@NonNull private final TimeInterpolator amplitudeOffInterpolator;

DeterminateDrawable(
@NonNull Context context,
Expand Down Expand Up @@ -89,6 +106,25 @@ public final class DeterminateDrawable<S extends BaseProgressIndicatorSpec>
phaseAnimator.start();
}

// Initializes a linear animator to turn on/off wave amplitude.
amplitudeOnInterpolator =
MotionUtils.resolveThemeInterpolator(
context, R.attr.motionEasingStandardInterpolator, AnimationUtils.LINEAR_INTERPOLATOR);
amplitudeOffInterpolator =
MotionUtils.resolveThemeInterpolator(
context,
R.attr.motionEasingEmphasizedAccelerateInterpolator,
AnimationUtils.LINEAR_INTERPOLATOR);
amplitudeAnimator = new ValueAnimator();
amplitudeAnimator.setDuration(AMPLITUDE_ANIMATION_DURATION_MS);
amplitudeAnimator.setFloatValues(0, 1);
amplitudeAnimator.setInterpolator(null);
amplitudeAnimator.addUpdateListener(
animation -> {
activeIndicator.amplitudeFraction =
amplitudeInterpolator.getInterpolation(amplitudeAnimator.getAnimatedFraction());
});

setGrowFraction(1f);
}

Expand Down Expand Up @@ -211,9 +247,11 @@ public void jumpToCurrentState() {
*/
@Override
protected boolean onLevelChange(int level) {
float nextAmplitudeFraction = getAmplitudeFractionFromLevel(level);
if (skipAnimationOnLevelChange) {
springAnimation.skipToEnd();
setIndicatorFraction((float) level / MAX_DRAWABLE_LEVEL);
setAmplitudeFraction(nextAmplitudeFraction);
} else {
springAnimation.setStartValue(getIndicatorFraction() * MAX_DRAWABLE_LEVEL);
springAnimation.animateToFinalPosition(level);
Expand Down Expand Up @@ -242,6 +280,30 @@ void setLevelByFraction(float fraction) {
setLevel((int) (MAX_DRAWABLE_LEVEL * fraction));
}

private float getAmplitudeFractionFromLevel(int level) {
return level >= FULL_AMPLITUDE_FRACTION_MIN * MAX_DRAWABLE_LEVEL
&& level <= FULL_AMPLITUDE_FRACTION_MAX * MAX_DRAWABLE_LEVEL
? 1f
: 0f;
}

private void maybeStartAmplitudeAnimator(int level) {
float newAmplitudeFraction = getAmplitudeFractionFromLevel(level);
if (newAmplitudeFraction != targetAmplitudeFraction) {
if (amplitudeAnimator.isRunning()) {
amplitudeAnimator.cancel();
}
targetAmplitudeFraction = newAmplitudeFraction;
if (targetAmplitudeFraction == 1f) {
amplitudeInterpolator = amplitudeOnInterpolator;
amplitudeAnimator.start();
} else {
amplitudeInterpolator = amplitudeOffInterpolator;
amplitudeAnimator.reverse();
}
}
}

// ******************* Drawing methods *******************

@Override
Expand Down Expand Up @@ -308,6 +370,11 @@ private void setIndicatorFraction(float indicatorFraction) {
invalidateSelf();
}

private void setAmplitudeFraction(float amplitudeFraction) {
this.activeIndicator.amplitudeFraction = amplitudeFraction;
invalidateSelf();
}

@NonNull
DrawingDelegate<S> getDrawingDelegate() {
return drawingDelegate;
Expand Down Expand Up @@ -337,6 +404,7 @@ public float getValue(DeterminateDrawable<?> drawable) {
@Override
public void setValue(DeterminateDrawable<?> drawable, float value) {
drawable.setIndicatorFraction(value / MAX_DRAWABLE_LEVEL);
drawable.maybeStartAmplitudeAnimator((int) value);
}
};
}
Expand Up @@ -169,6 +169,10 @@ protected static class ActiveIndicator {
// to each other. Gaps are needed in this case.
@Px int gapSize;

// The fraction [0, 1] of the amplitude on indicator.
@FloatRange(from = 0.0, to = 1.0)
float amplitudeFraction = 1;

// The fraction [0, 1] of the initial phase [0, 2 * PI] on indicator.
@FloatRange(from = 0.0, to = 1.0)
float phaseFraction;
Expand Down
Expand Up @@ -158,6 +158,7 @@ void fillIndicator(
color,
activeIndicator.gapSize,
activeIndicator.gapSize,
activeIndicator.amplitudeFraction,
activeIndicator.phaseFraction,
/* drawingActiveIndicator= */ true);
}
Expand All @@ -180,6 +181,7 @@ void fillTrack(
color,
gapSize,
gapSize,
/* amplitudeFraction= */ 0f,
/* phaseFraction= */ 0f,
/* drawingActiveIndicator= */ false);
}
Expand All @@ -194,6 +196,7 @@ void fillTrack(
* @param paintColor The color used to draw the indicator.
* @param startGapSize The gap size applied to the start (left) of the drawing part.
* @param endGapSize The gap size applied to the end (right) of the drawing part.
* @param amplitudeFraction The fraction [0, 1] of amplitude applied to the part.
* @param phaseFraction The fraction [0, 1] of initial phase in one cycle.
* @param drawingActiveIndicator Whether this part should be drawn as an active indicator.
*/
Expand All @@ -205,6 +208,7 @@ private void drawLine(
@ColorInt int paintColor,
@Px int startGapSize,
@Px int endGapSize,
float amplitudeFraction,
float phaseFraction,
boolean drawingActiveIndicator) {
startFraction = clamp(startFraction, 0f, 1f);
Expand All @@ -230,7 +234,7 @@ private void drawLine(
// Adjusts start/end X so the progress indicator will start from 0 when startFraction == 0.
float originX = -trackLength / 2;

boolean drawWavyPath = spec.hasWavyEffect() && drawingActiveIndicator;
boolean drawWavyPath = spec.hasWavyEffect() && drawingActiveIndicator && amplitudeFraction > 0f;

// No need to draw on track if start and end are out of visible range.
if (startPx <= endPx) {
Expand Down Expand Up @@ -275,6 +279,7 @@ private void drawLine(
displayedActivePath,
startBlockCenterX / trackLength,
endBlockCenterX / trackLength,
amplitudeFraction,
phaseFraction);
canvas.drawPath(displayedActivePath, paint);
}
Expand Down Expand Up @@ -382,6 +387,7 @@ private Pair<PathPoint, PathPoint> getDisplayedPath(
@NonNull Path displayedPath,
float start,
float end,
float amplitudeFraction,
float phaseFraction) {
displayedPath.rewind();
float resultTranslationX = -trackLength / 2;
Expand All @@ -407,7 +413,7 @@ private Pair<PathPoint, PathPoint> getDisplayedPath(
startPoint.translate(resultTranslationX, 0);
endPoint.translate(resultTranslationX, 0);
if (spec.hasWavyEffect()) {
float scaleY = displayedAmplitude;
float scaleY = displayedAmplitude * amplitudeFraction;
transform.postScale(1, scaleY);
startPoint.scale(1, scaleY);
endPoint.scale(1, scaleY);
Expand Down

0 comments on commit 23bcd50

Please sign in to comment.