diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e5a7bc288560e..1796c7b971a9a 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -98,6 +98,7 @@ package android.animation { public abstract class Animator implements java.lang.Cloneable { method public static long getBackgroundPauseDelay(); + method public static void setAnimatorPausingEnabled(boolean); method public static void setBackgroundPauseDelay(long); } diff --git a/core/java/android/animation/AnimationHandler.java b/core/java/android/animation/AnimationHandler.java index 7f6df2261fcc8..57ab55a53e468 100644 --- a/core/java/android/animation/AnimationHandler.java +++ b/core/java/android/animation/AnimationHandler.java @@ -53,6 +53,9 @@ public class AnimationHandler { new ArrayList<>(); private AnimationFrameCallbackProvider mProvider; + // Static flag which allows the pausing behavior to be globally disabled/enabled. + private static boolean sAnimatorPausingEnabled = true; + /** * This paused list is used to store animators forcibly paused when the activity * went into the background (to avoid unnecessary background processing work). @@ -93,6 +96,15 @@ public class AnimationHandler { return sAnimatorHandler.get(); } + /** + * Disable the default behavior of pausing infinite animators when + * apps go into the background. + * + * @param enable Enable (default behavior) or disable background pausing behavior. + */ + public static void setAnimatorPausingEnabled(boolean enable) { + sAnimatorPausingEnabled = enable; + } /** * This is called when a window goes away. We should remove @@ -136,16 +148,19 @@ public class AnimationHandler { } else { mAnimatorRequestors.remove(requestor); } + if (!sAnimatorPausingEnabled) { + // Resume any animators that have been paused in the meantime, otherwise noop + // Leave logic above so that if pausing gets re-enabled, the state of the requestors + // list is valid + resumeAnimators(); + return; + } boolean isEmpty = mAnimatorRequestors.isEmpty(); if (wasEmpty != isEmpty) { // only paused/resume animators if there was a visibility change if (!isEmpty) { // If any requestors are enabled, resume currently paused animators - Choreographer.getInstance().removeFrameCallback(mPauser); - for (int i = mPausedAnimators.size() - 1; i >= 0; --i) { - mPausedAnimators.get(i).resume(); - } - mPausedAnimators.clear(); + resumeAnimators(); } else { // Wait before pausing to avoid thrashing animator state for temporary backgrounding Choreographer.getInstance().postFrameCallbackDelayed(mPauser, @@ -160,6 +175,14 @@ public class AnimationHandler { } } + private void resumeAnimators() { + Choreographer.getInstance().removeFrameCallback(mPauser); + for (int i = mPausedAnimators.size() - 1; i >= 0; --i) { + mPausedAnimators.get(i).resume(); + } + mPausedAnimators.clear(); + } + private Choreographer.FrameCallback mPauser = frameTimeNanos -> { if (mAnimatorRequestors.size() > 0) { // something enabled animators since this callback was scheduled - bail diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index 9e55d359b416f..f69decb087f31 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -92,6 +92,20 @@ public abstract class Animator implements Cloneable { return sBackgroundPauseDelay; } + /** + * Sets the behavior of animator pausing when apps go into the background. + * This is exposed as a test API for verification, but is intended for use by internal/ + * platform code, potentially for use by a system property that could disable it + * system wide. + * + * @param enable Enable (default behavior) or disable background pausing behavior. + * @hide + */ + @TestApi + public static void setAnimatorPausingEnabled(boolean enable) { + AnimationHandler.setAnimatorPausingEnabled(enable); + } + /** * Starts this animation. If the animation has a nonzero startDelay, the animation will start * running after that delay elapses. A non-delayed animation will have its initial