Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60442cb1 authored by Sally's avatar Sally
Browse files

Add API to read and listen to duration scale changes for animations

Developers and accessibility services can remove animations by
changing the duration scale to zero. Physics-based animation
libraries like AndroidX's dynamic animations aren't duration-based,
but they need to know the duration scale to remove animations.

Expose the scale through ValueAnimator to avoid requests for
Context which may not be fulfilled by the developer.

Unhide test API getDurationScale and handle callbacks on the main
thread.

Test: atest ValueAnimatorTest
Bug: 221303983
Change-Id: I3134e0bf007df046a9a2aa0f9d866c27c7989e68
parent 99483fa8
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -3901,6 +3901,7 @@ package android.animation {
    method public Object getAnimatedValue(String);
    method public long getCurrentPlayTime();
    method public long getDuration();
    method @FloatRange(from=0, to=1) public static float getDurationScale();
    method public static long getFrameDelay();
    method public int getRepeatCount();
    method public int getRepeatMode();
@@ -3912,6 +3913,7 @@ package android.animation {
    method public static android.animation.ValueAnimator ofInt(int...);
    method public static android.animation.ValueAnimator ofObject(android.animation.TypeEvaluator, java.lang.Object...);
    method public static android.animation.ValueAnimator ofPropertyValuesHolder(android.animation.PropertyValuesHolder...);
    method public static boolean registerDurationScaleChangeListener(@NonNull android.animation.ValueAnimator.DurationScaleChangeListener);
    method public void removeAllUpdateListeners();
    method public void removeUpdateListener(android.animation.ValueAnimator.AnimatorUpdateListener);
    method public void reverse();
@@ -3928,6 +3930,7 @@ package android.animation {
    method public void setRepeatMode(int);
    method public void setStartDelay(long);
    method public void setValues(android.animation.PropertyValuesHolder...);
    method public static boolean unregisterDurationScaleChangeListener(@NonNull android.animation.ValueAnimator.DurationScaleChangeListener);
    field public static final int INFINITE = -1; // 0xffffffff
    field public static final int RESTART = 1; // 0x1
    field public static final int REVERSE = 2; // 0x2
@@ -3937,6 +3940,10 @@ package android.animation {
    method public void onAnimationUpdate(@NonNull android.animation.ValueAnimator);
  }
  public static interface ValueAnimator.DurationScaleChangeListener {
    method public void onChanged(float);
  }
}
package android.annotation {
+1 −2
Original line number Diff line number Diff line
@@ -95,8 +95,7 @@ package android.accessibilityservice {
package android.animation {

  public class ValueAnimator extends android.animation.Animator {
    method public static float getDurationScale();
    method public static void setDurationScale(float);
    method @MainThread public static void setDurationScale(@FloatRange(from=0, to=1) float);
  }

}
+97 −4
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@
package android.animation;

import android.annotation.CallSuper;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -35,8 +37,10 @@ import android.view.animation.LinearInterpolator;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * This class provides a simple timing engine for running animations
@@ -91,6 +95,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    private static float sDurationScale = 1.0f;

    private static final ArrayList<WeakReference<DurationScaleChangeListener>>
            sDurationScaleChangeListeners = new ArrayList<>();

    /**
     * Internal variables
     * NOTE: This object implements the clone() method, making a deep copy of any referenced
@@ -308,19 +315,91 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
     */
    @UnsupportedAppUsage
    @TestApi
    public static void setDurationScale(float durationScale) {
    @MainThread
    public static void setDurationScale(@FloatRange(from = 0, to = 1) float durationScale) {
        sDurationScale = durationScale;
        List<WeakReference<DurationScaleChangeListener>> listenerCopy;

        synchronized (sDurationScaleChangeListeners) {
            listenerCopy = new ArrayList<>(sDurationScaleChangeListeners);
        }

        for (WeakReference<DurationScaleChangeListener> listenerRef : listenerCopy) {
            final DurationScaleChangeListener listener = listenerRef.get();
            if (listener != null) {
                listener.onChanged(durationScale);
            }
        }
    }

    /**
     * @hide
     * Returns the system-wide scaling factor for Animator-based animations.
     *
     * This affects both the start delay and duration of all such animations. Setting to 0 will
     * cause animations to end immediately. The default value is 1.0f.
     *
     * @return the duration scale.
     */
    @UnsupportedAppUsage
    @TestApi
    @FloatRange(from = 0, to = 1)
    public static float getDurationScale() {
        return sDurationScale;
    }

    /**
     * Registers a {@link DurationScaleChangeListener}
     *
     * This listens for changes to the system-wide scaling factor for Animator-based animations.
     * Listeners will be called on the main thread.
     *
     * @param listener the listener to register.
     * @return true if the listener was registered.
     */
    public static boolean registerDurationScaleChangeListener(
            @NonNull DurationScaleChangeListener listener) {
        int posToReplace = -1;
        synchronized (sDurationScaleChangeListeners) {
            for (int i = 0; i < sDurationScaleChangeListeners.size(); i++) {
                final WeakReference<DurationScaleChangeListener> ref =
                        sDurationScaleChangeListeners.get(i);
                if (ref.get() == null) {
                    if (posToReplace == -1) {
                        posToReplace = i;
                    }
                } else if (ref.get() == listener) {
                    return false;
                }
            }
            if (posToReplace != -1) {
                sDurationScaleChangeListeners.set(posToReplace, new WeakReference<>(listener));
                return true;
            } else {
                return sDurationScaleChangeListeners.add(new WeakReference<>(listener));
            }
        }
    }

    /**
     * Unregisters a DurationScaleChangeListener.
     *
     * @see #registerDurationScaleChangeListener(DurationScaleChangeListener)
     * @param listener the listener to unregister.
     * @return true if the listener was unregistered.
     */
    public static boolean unregisterDurationScaleChangeListener(
            @NonNull DurationScaleChangeListener listener) {
        synchronized (sDurationScaleChangeListeners) {
            WeakReference<DurationScaleChangeListener> listenerRefToRemove = null;
            for (WeakReference<DurationScaleChangeListener> listenerRef :
                    sDurationScaleChangeListeners) {
                if (listenerRef.get() == listener) {
                    listenerRefToRemove = listenerRef;
                    break;
                }
            }
            return sDurationScaleChangeListeners.remove(listenerRefToRemove);
        }
    }

    /**
     * Returns whether animators are currently enabled, system-wide. By default, all
     * animators are enabled. This can change if either the user sets a Developer Option
@@ -1709,4 +1788,18 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
    public void setAnimationHandler(@Nullable AnimationHandler animationHandler) {
        mAnimationHandler = animationHandler;
    }

    /**
     * Listener interface for the system-wide scaling factor for Animator-based animations.
     *
     * @see #registerDurationScaleChangeListener(DurationScaleChangeListener)
     * @see #unregisterDurationScaleChangeListener(DurationScaleChangeListener)
     */
    public interface DurationScaleChangeListener {
        /**
         * Called when the duration scale changes.
         * @param scale the duration scalel
         */
        void onChanged(float scale);
    }
}