• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.animation
18 
19 import android.animation.Animator
20 import java.util.function.Consumer
21 import org.junit.rules.RuleChain
22 import org.junit.rules.TestRule
23 import org.junit.runner.Description
24 import org.junit.runners.model.Statement
25 
26 /**
27  * A rule that wraps both [androidx.core.animation.AnimatorTestRule] and
28  * [android.animation.AnimatorTestRule] such that the clocks of the two animation handlers can be
29  * advanced together.
30  *
31  * @param test the instance of the test used to look up the TestableLooper.  If a TestableLooper is
32  * found, the time can only be advanced on that thread; otherwise the time must be advanced on the
33  * main thread.
34  */
35 class AnimatorTestRule(test: Any?) : TestRule {
36     // Create the androidx rule, which initializes start time to SystemClock.uptimeMillis(),
37     // then copy that time to the platform rule so that the two clocks are in sync.
38     private val androidxRule = androidx.core.animation.AnimatorTestRule()
39     private val platformRule = android.animation.AnimatorTestRule(test, androidxRule.startTime)
40     private val advanceAndroidXTimeBy =
41         Consumer<Long> { timeDelta -> androidxRule.advanceTimeBy(timeDelta) }
42 
43     /** Access the mStartTime field; bypassing the restriction of being on a looper thread. */
44     private val androidx.core.animation.AnimatorTestRule.startTime: Long
45         get() =
46             javaClass.getDeclaredField("mStartTime").let { field ->
47                 field.isAccessible = true
48                 field.getLong(this)
49             }
50 
51     /**
52      * Chain is for simplicity not to force a particular order; order should not matter, because
53      * each rule affects a different AnimationHandler classes, and no callbacks to code under test
54      * should be triggered by these rules
55      */
56     private val ruleChain = RuleChain.emptyRuleChain().around(androidxRule).around(platformRule)
57 
58     override fun apply(base: Statement, description: Description): Statement =
59         ruleChain.apply(base, description)
60 
61     /**
62      * Advances the animation clock by the given amount of delta in milliseconds. This call will
63      * produce an animation frame to all the ongoing animations.
64      *
65      * @param timeDelta the amount of milliseconds to advance
66      */
67     fun advanceTimeBy(timeDelta: Long) {
68         // NOTE: To avoid errors with order, we have to ensure that we advance the time within both
69         //  rules before either rule does its frame output. Failing to do this could cause the
70         //  animation from one to start later than the other.
71         platformRule.advanceTimeBy(timeDelta, advanceAndroidXTimeBy)
72     }
73 
74     /**
75      * This is similar to [advanceTimeBy] but it expects to reach the end of an animation. This call
76      * may produce 2 frames for the last animation frame and end animation callback.
77      *
78      * @param durationMs the duration that is greater than or equal to the animation duration.
79      */
80     fun advanceAnimationDuration(durationMs: Long) {
81         advanceTimeBy(durationMs)
82         if (Animator.isPostNotifyEndListenerEnabled()) {
83             // If the post-end-callback is enabled, the AnimatorListener#onAnimationEnd will be
84             // called on the next frame of last animation frame. So trigger additional doFrame to
85             // ensure the end callback method is called (by android.animation.AnimatorTestRule).
86             advanceTimeBy(0)
87         }
88     }
89 
90     /**
91      * Returns the current time in milliseconds tracked by the AnimationHandlers. Note that this is
92      * a different time than the time tracked by {@link SystemClock}.
93      */
94     val currentTime: Long
95         get() = androidxRule.currentTime
96 }
97