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