Skip to content

Commit f7bad9a

Browse files
pekingmehunterstich
authored andcommitted
[ProgressIndicator] Changed drawables to detect system animator duration scale by themselves.
PiperOrigin-RevId: 322193517
1 parent a6774de commit f7bad9a

File tree

5 files changed

+105
-78
lines changed

5 files changed

+105
-78
lines changed

catalog/java/io/material/catalog/progressindicator/ProgressIndicatorCustomDemoFragment.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,15 @@ public void initialize(@NonNull View view) {
6868

6969
indeterminateIndicator.initializeDrawables(
7070
new IndeterminateDrawable(
71+
getContext(),
7172
indeterminateIndicator.getSpec(),
7273
new WavyDrawingDelegate(),
7374
new LinearIndeterminateNonSeamlessAnimatorDelegate(getContext())),
7475
null);
7576
determinateIndicator.initializeDrawables(
76-
null, new DeterminateDrawable(determinateIndicator.getSpec(), new WavyDrawingDelegate()));
77+
null,
78+
new DeterminateDrawable(
79+
getContext(), determinateIndicator.getSpec(), new WavyDrawingDelegate()));
7780

7881
Slider slider = view.findViewById(R.id.slider);
7982
Button showButton = view.findViewById(R.id.show_button);

lib/java/com/google/android/material/progressindicator/DeterminateDrawable.java

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.google.android.material.progressindicator;
1818

19+
import android.content.Context;
1920
import android.graphics.Canvas;
2021
import android.graphics.Rect;
2122
import androidx.annotation.NonNull;
@@ -42,12 +43,14 @@ public final class DeterminateDrawable extends DrawableWithAnimatedVisibilityCha
4243
private final SpringAnimation springAnimator;
4344
// Fraction of displayed indicator in the total width.
4445
private float indicatorFraction;
45-
// Whether to skip the next level change event.
46-
private boolean skipNextLevelChange = false;
46+
// Whether to skip the spring animation on level change event.
47+
private boolean skipAnimationOnLevelChange = false;
4748

4849
public DeterminateDrawable(
49-
@NonNull ProgressIndicatorSpec spec, @NonNull DrawingDelegate drawingDelegate) {
50-
super(spec);
50+
@NonNull Context context,
51+
@NonNull ProgressIndicatorSpec spec,
52+
@NonNull DrawingDelegate drawingDelegate) {
53+
super(context, spec);
5154

5255
this.drawingDelegate = drawingDelegate;
5356

@@ -69,21 +72,6 @@ public void onAnimationUpdate(DynamicAnimation animation,
6972
setGrowFraction(1f);
7073
}
7174

72-
/**
73-
* Invalidates the spring animation's duration by changing the stiffness of {@link SpringForce}.
74-
* Negative scale is invalid. 0 is handled as a special case in {@link ProgressIndicator}, which
75-
* will skip the animation when the level changes.
76-
*
77-
* @param scale New scale of the animator's duration.
78-
* @throws IllegalArgumentException if scale is negative or zero.
79-
*/
80-
void invalidateAnimationScale(float scale) {
81-
if (scale <= 0) {
82-
throw new IllegalArgumentException("Animation duration scale must be positive.");
83-
}
84-
springForce.setStiffness(SPRING_FORCE_STIFFNESS / scale);
85-
}
86-
8775
/**
8876
* Sets the drawable level with a fraction [0,1] of the progress. Note: this function is not used
8977
* to force updating the level in opposite to the automatic level updates by framework {@link
@@ -95,13 +83,35 @@ void setLevelByFraction(float fraction) {
9583
setLevel((int) (MAX_DRAWABLE_LEVEL * fraction));
9684
}
9785

98-
/** Sets the flag so that when the level is changed next time, it will skip the animation. */
99-
void skipNextLevelChange() {
100-
skipNextLevelChange = true;
101-
}
102-
10386
// ******************* Overridden methods *******************
10487

88+
/**
89+
* Sets the visibility of this drawable. It calls the {@link
90+
* DrawableWithAnimatedVisibilityChange#setVisible(boolean, boolean)} to start the show/hide
91+
* animation properly. The spring animation will be skipped when the level changes, if animation
92+
* is not requested.
93+
*
94+
* @param visible Whether to make the drawable visible.
95+
* @param animationDesired Whether to change the visibility with animation.
96+
* @return {@code true}, if the visibility changes or will change after the animation; {@code
97+
* false}, otherwise.
98+
*/
99+
@Override
100+
public boolean setVisible(boolean visible, boolean animationDesired) {
101+
boolean changed = super.setVisible(visible, animationDesired);
102+
103+
float systemAnimatorDurationScale =
104+
animatorDurationScaleProvider.getSystemAnimatorDurationScale(context.getContentResolver());
105+
if (systemAnimatorDurationScale == 0) {
106+
skipAnimationOnLevelChange = true;
107+
} else {
108+
skipAnimationOnLevelChange = false;
109+
springForce.setStiffness(SPRING_FORCE_STIFFNESS / systemAnimatorDurationScale);
110+
}
111+
112+
return changed;
113+
}
114+
105115
/** Skips the animation of changing indicator length, directly displays the target progress. */
106116
@Override
107117
public void jumpToCurrentState() {
@@ -119,11 +129,9 @@ public void jumpToCurrentState() {
119129
*/
120130
@Override
121131
protected boolean onLevelChange(int level) {
122-
if (skipNextLevelChange) {
132+
if (skipAnimationOnLevelChange) {
123133
springAnimator.cancel();
124134
setIndicatorFraction((float) level / MAX_DRAWABLE_LEVEL);
125-
// One time usage.
126-
skipNextLevelChange = false;
127135
} else {
128136
springAnimator.setStartValue(getIndicatorFraction() * MAX_DRAWABLE_LEVEL);
129137
springAnimator.animateToFinalPosition(level);

lib/java/com/google/android/material/progressindicator/DrawableWithAnimatedVisibilityChange.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.animation.AnimatorListenerAdapter;
2020
import android.animation.ObjectAnimator;
2121
import android.animation.ValueAnimator;
22+
import android.content.Context;
2223
import android.graphics.ColorFilter;
2324
import android.graphics.Paint;
2425
import android.graphics.PixelFormat;
@@ -44,8 +45,12 @@ abstract class DrawableWithAnimatedVisibilityChange extends Drawable implements
4445
// Animation duration for both show and hide animators.
4546
private static final int GROW_DURATION = 500;
4647

48+
// The current context this drawable is running in.
49+
final Context context;
4750
// The spec of the component being served.
4851
final ProgressIndicatorSpec spec;
52+
// Utils class.
53+
AnimatorDurationScaleProvider animatorDurationScaleProvider;
4954

5055
// ValueAnimator used for show animation.
5156
private ValueAnimator showAnimator;
@@ -67,8 +72,11 @@ abstract class DrawableWithAnimatedVisibilityChange extends Drawable implements
6772

6873
// ******************* Constructor *******************
6974

70-
DrawableWithAnimatedVisibilityChange(@NonNull ProgressIndicatorSpec spec) {
75+
DrawableWithAnimatedVisibilityChange(
76+
@NonNull Context context, @NonNull ProgressIndicatorSpec spec) {
77+
this.context = context;
7178
this.spec = spec;
79+
animatorDurationScaleProvider = new AnimatorDurationScaleProvider();
7280

7381
setAlpha(255);
7482

@@ -187,6 +195,10 @@ public boolean isRunning() {
187195
*/
188196
@Override
189197
public boolean setVisible(boolean visible, boolean animationDesired) {
198+
float systemAnimatorDurationScale =
199+
animatorDurationScaleProvider.getSystemAnimatorDurationScale(context.getContentResolver());
200+
animationDesired &= systemAnimatorDurationScale > 0;
201+
190202
// If the drawable is visible and not being hidden, prevents to start the show animation.
191203
if (visible && animationDesired && isVisible() && !hideAnimator.isRunning()) {
192204
return false;

lib/java/com/google/android/material/progressindicator/IndeterminateDrawable.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import android.animation.Animator;
1919
import android.animation.AnimatorListenerAdapter;
2020
import android.animation.AnimatorSet;
21+
import android.content.Context;
2122
import android.graphics.Canvas;
2223
import android.graphics.Rect;
2324
import androidx.annotation.NonNull;
@@ -34,10 +35,11 @@ public final class IndeterminateDrawable extends DrawableWithAnimatedVisibilityC
3435
private IndeterminateAnimatorDelegate<AnimatorSet> animatorDelegate;
3536

3637
public IndeterminateDrawable(
38+
@NonNull Context context,
3739
@NonNull ProgressIndicatorSpec spec,
3840
@NonNull DrawingDelegate drawingDelegate,
3941
@NonNull IndeterminateAnimatorDelegate<AnimatorSet> animatorDelegate) {
40-
super(spec);
42+
super(context, spec);
4143

4244
this.drawingDelegate = drawingDelegate;
4345
setAnimatorDelegate(animatorDelegate);
@@ -48,12 +50,12 @@ public IndeterminateDrawable(
4850
/**
4951
* Sets the visibility of this drawable. It calls the {@link
5052
* DrawableWithAnimatedVisibilityChange#setVisible(boolean, boolean)} to start the show/hide
51-
* animation properly. If it's requested to be visible, the main animator will be started. If it's
52-
* required to be invisible, the main animator will be canceled unless the hide animation is
53-
* required.
53+
* animation properly. The indeterminate animation will be started if animation is requested.
5454
*
5555
* @param visible Whether to make the drawable visible.
5656
* @param animationDesired Whether to change the visibility with animation.
57+
* @return {@code true}, if the visibility changes or will change after the animation; {@code
58+
* false}, otherwise.
5759
*/
5860
@Override
5961
public boolean setVisible(boolean visible, boolean animationDesired) {
@@ -65,7 +67,11 @@ public boolean setVisible(boolean visible, boolean animationDesired) {
6567
animatorDelegate.resetPropertiesForNewStart();
6668
}
6769
// Restarts the main animator if it's visible and needs to be animated.
68-
if (visible && animationDesired) {
70+
if (visible
71+
&& animationDesired
72+
&& animatorDurationScaleProvider.getSystemAnimatorDurationScale(
73+
context.getContentResolver())
74+
!= 0) {
6975
animatorDelegate.startAnimator();
7076
}
7177

lib/java/com/google/android/material/progressindicator/ProgressIndicator.java

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,6 @@ public class ProgressIndicator extends ProgressBar {
121121

122122
private long lastShowStartTime = -1L;
123123

124-
/** The scale of the animation speed combining system setting and debug parameters. */
125-
private float systemAnimationScale = 1f;
126-
127124
private AnimatorDurationScaleProvider animatorDurationScaleProvider;
128125

129126
// ******************** Interfaces **********************
@@ -270,14 +267,15 @@ private void initializeDrawables() {
270267
isLinearSeamless()
271268
? new LinearIndeterminateSeamlessAnimatorDelegate()
272269
: new LinearIndeterminateNonSeamlessAnimatorDelegate(getContext());
273-
setIndeterminateDrawable(new IndeterminateDrawable(spec, drawingDelegate, animatorDelegate));
274-
setProgressDrawable(new DeterminateDrawable(spec, drawingDelegate));
270+
setIndeterminateDrawable(
271+
new IndeterminateDrawable(getContext(), spec, drawingDelegate, animatorDelegate));
272+
setProgressDrawable(new DeterminateDrawable(getContext(), spec, drawingDelegate));
275273
} else {
276274
DrawingDelegate drawingDelegate = new CircularDrawingDelegate();
277275
setIndeterminateDrawable(
278276
new IndeterminateDrawable(
279-
spec, drawingDelegate, new CircularIndeterminateAnimatorDelegate()));
280-
setProgressDrawable(new DeterminateDrawable(spec, drawingDelegate));
277+
getContext(), spec, drawingDelegate, new CircularIndeterminateAnimatorDelegate()));
278+
setProgressDrawable(new DeterminateDrawable(getContext(), spec, drawingDelegate));
281279
}
282280

283281
applyNewVisibility();
@@ -343,17 +341,6 @@ private void unregisterAnimationCallbacks() {
343341
}
344342
}
345343

346-
private void updateAnimationScale() {
347-
systemAnimationScale =
348-
animatorDurationScaleProvider.getSystemAnimatorDurationScale(
349-
getContext().getContentResolver());
350-
if (systemAnimationScale > 0) {
351-
if (getProgressDrawable() != null) {
352-
getProgressDrawable().invalidateAnimationScale(systemAnimationScale);
353-
}
354-
}
355-
}
356-
357344
// ******************** Visibility control **********************
358345

359346
/**
@@ -394,13 +381,12 @@ public void hide() {
394381
*/
395382
private void internalHide() {
396383
// Hides animation should be used if it's visible to user and potentially can be hidden with
397-
// animation, unless animators are disabled actively.
398-
boolean shouldHideAnimated =
399-
visibleToUser() && spec.growMode != GROW_MODE_NONE && !isAnimatorDisabled();
384+
// animation.
385+
boolean shouldHideAnimated = visibleToUser() && spec.growMode != GROW_MODE_NONE;
400386

401387
getCurrentDrawable().setVisible(false, shouldHideAnimated);
402388

403-
if (!shouldHideAnimated) {
389+
if (isNoLongerNeedToBeVisible()) {
404390
setVisibility(INVISIBLE);
405391
}
406392
}
@@ -426,14 +412,12 @@ private void applyNewVisibility() {
426412
return;
427413
}
428414

429-
updateAnimationScale();
430-
431415
boolean visibleToUser = visibleToUser();
432416

433417
// Sets the drawable to visible/invisible if the component is currently visible/invisible. Only
434418
// show animation should be started (when the component is currently visible). Hide animation
435419
// should have already ended or is not necessary at this point.
436-
getCurrentDrawable().setVisible(visibleToUser, visibleToUser && !isAnimatorDisabled());
420+
getCurrentDrawable().setVisible(visibleToUser, visibleToUser);
437421
}
438422

439423
@Override
@@ -550,14 +534,17 @@ public DrawingDelegate getCurrentDrawingDelegate() {
550534
*/
551535
@Override
552536
public void setProgressDrawable(Drawable drawable) {
553-
if (drawable == null || drawable instanceof DeterminateDrawable) {
537+
if (drawable == null) {
538+
super.setProgressDrawable(null);
539+
return;
540+
}
541+
if (drawable instanceof DeterminateDrawable) {
542+
drawable.setVisible(false, false);
554543
super.setProgressDrawable(drawable);
555544
// Every time ProgressBar sets progress drawable, it refreshes the drawable's level with
556545
// progress then secondary progress. Since secondary progress is not used here. We need to set
557546
// the level actively to overcome the affects from secondary progress.
558-
if (drawable != null) {
559-
((DeterminateDrawable) drawable).setLevelByFraction((float) getProgress() / getMax());
560-
}
547+
((DeterminateDrawable) drawable).setLevelByFraction((float) getProgress() / getMax());
561548
} else {
562549
throw new IllegalArgumentException("Cannot set framework drawable as progress drawable.");
563550
}
@@ -570,7 +557,12 @@ public void setProgressDrawable(Drawable drawable) {
570557
*/
571558
@Override
572559
public void setIndeterminateDrawable(Drawable drawable) {
573-
if (drawable == null || drawable instanceof IndeterminateDrawable) {
560+
if (drawable == null) {
561+
super.setIndeterminateDrawable(null);
562+
return;
563+
}
564+
if (drawable instanceof IndeterminateDrawable) {
565+
drawable.setVisible(false, false);
574566
super.setIndeterminateDrawable(drawable);
575567
} else {
576568
throw new IllegalArgumentException(
@@ -644,9 +636,13 @@ private void updateColorsInDrawables() {
644636
getIndeterminateDrawable().getAnimatorDelegate().invalidateSpecValues();
645637
}
646638

647-
/** Returns whether the animators are disabled passively (by system settings). */
648-
private boolean isAnimatorDisabled() {
649-
return systemAnimationScale == 0;
639+
/**
640+
* Returns {@code true} if both drawables are either null or not visible; {@code false},
641+
* otherwise.
642+
*/
643+
private boolean isNoLongerNeedToBeVisible() {
644+
return (getProgressDrawable() == null || !getProgressDrawable().isVisible())
645+
&& (getIndeterminateDrawable() == null || !getIndeterminateDrawable().isVisible());
650646
}
651647

652648
// ******************** Getters and setters **********************
@@ -991,33 +987,35 @@ public void setProgressCompat(int progress, boolean animated) {
991987
storedProgress = progress;
992988
storedProgressAnimated = animated;
993989

994-
updateAnimationScale();
995-
996-
if (isAnimatorDisabled()) {
990+
if (animatorDurationScaleProvider.getSystemAnimatorDurationScale(
991+
getContext().getContentResolver())
992+
== 0) {
997993
switchIndeterminateModeCallback.onAnimationEnd(getIndeterminateDrawable());
998994
} else {
999995
getIndeterminateDrawable().getAnimatorDelegate().requestCancelAnimatorAfterCurrentCycle();
1000996
}
1001997
return;
1002998
}
1003999

1004-
// When no progress animation is needed, it will notify the drawable to skip animation on the
1005-
// next level change.
1006-
if (getProgressDrawable() != null
1007-
&& getProgress() != progress
1008-
&& (!animated || isAnimatorDisabled())) {
1009-
getProgressDrawable().skipNextLevelChange();
1010-
}
1011-
10121000
// Calls ProgressBar setProgress(int) to update the progress value and level. We don't rely on
10131001
// it to draw or animate the indicator.
10141002
super.setProgress(progress);
1003+
// Fast forward to the final state of the determinate animation.
1004+
if (getProgressDrawable() != null && !animated) {
1005+
getProgressDrawable().jumpToCurrentState();
1006+
}
10151007
}
10161008

10171009
@VisibleForTesting
10181010
public void setAnimatorDurationScaleProvider(
10191011
@NonNull AnimatorDurationScaleProvider animatorDurationScaleProvider) {
10201012
this.animatorDurationScaleProvider = animatorDurationScaleProvider;
1013+
if (getProgressDrawable() != null) {
1014+
getProgressDrawable().animatorDurationScaleProvider = animatorDurationScaleProvider;
1015+
}
1016+
if (getIndeterminateDrawable() != null) {
1017+
getIndeterminateDrawable().animatorDurationScaleProvider = animatorDurationScaleProvider;
1018+
}
10211019
}
10221020

10231021
// ************************ In-place defined parameters ****************************

0 commit comments

Comments
 (0)