diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 71598938f42f7..603272e3df1db 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -151,6 +151,7 @@ android_library { ], static_libs: [ "androidx.appcompat_appcompat", + "androidx.core_core-animation", "androidx.arch.core_core-runtime", "androidx-constraintlayout_constraintlayout", "androidx.dynamicanimation_dynamicanimation", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java index 798250de89d0d..26edd7d2268bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java @@ -108,6 +108,20 @@ public class FlingAnimationUtils { apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue)); } + /** + * Applies the interpolator and length to the animator, such that the fling animation is + * consistent with the finger motion. + * + * @param animator the animator to apply + * @param currValue the current value + * @param endValue the end value of the animator + * @param velocity the current velocity of the motion + */ + public void apply(androidx.core.animation.Animator animator, + float currValue, float endValue, float velocity) { + apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue)); + } + /** * Applies the interpolator and length to the animator, such that the fling animation is * consistent with the finger motion. @@ -141,6 +155,24 @@ public class FlingAnimationUtils { animator.setInterpolator(properties.mInterpolator); } + /** + * Applies the interpolator and length to the animator, such that the fling animation is + * consistent with the finger motion. + * + * @param animator the animator to apply + * @param currValue the current value + * @param endValue the end value of the animator + * @param velocity the current velocity of the motion + * @param maxDistance the maximum distance for this interaction; the maximum animation length + * gets multiplied by the ratio between the actual distance and this value + */ + public void apply(androidx.core.animation.Animator animator, + float currValue, float endValue, float velocity, float maxDistance) { + AnimatorProperties properties = getProperties(currValue, endValue, velocity, maxDistance); + animator.setDuration(properties.mDuration); + animator.setInterpolator(properties.getInterpolator()); + } + /** * Applies the interpolator and length to the animator, such that the fling animation is * consistent with the finger motion. @@ -367,6 +399,11 @@ public class FlingAnimationUtils { private static class AnimatorProperties { Interpolator mInterpolator; long mDuration; + + /** Get an AndroidX interpolator wrapper of the current mInterpolator */ + public androidx.core.animation.Interpolator getInterpolator() { + return mInterpolator::getInterpolation; + } } /** Builder for {@link #FlingAnimationUtils}. */ diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index f00615bc0fe67..7c377d2841e38 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -19,9 +19,6 @@ package com.android.systemui; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_ROW_EXPAND; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.content.Context; import android.util.FloatProperty; import android.util.Log; @@ -34,6 +31,11 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; +import androidx.annotation.NonNull; +import androidx.core.animation.Animator; +import androidx.core.animation.AnimatorListenerAdapter; +import androidx.core.animation.ObjectAnimator; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -616,7 +618,7 @@ public class ExpandHelper implements Gefingerpoken { public boolean mCancelled; @Override - public void onAnimationEnd(Animator animation) { + public void onAnimationEnd(@NonNull Animator animation) { if (!mCancelled) { mCallback.setUserExpandedChild(scaledView, expand); if (!mExpanding) { @@ -633,7 +635,7 @@ public class ExpandHelper implements Gefingerpoken { } @Override - public void onAnimationCancel(Animator animation) { + public void onAnimationCancel(@NonNull Animator animation) { mCancelled = true; } }); diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java index 5867a40c78fa7..44a2b682bf37a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java @@ -20,12 +20,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.animation.ObjectAnimator; import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import androidx.core.animation.AnimatorTestRule; +import androidx.core.animation.ObjectAnimator; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; @@ -37,6 +38,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,6 +47,9 @@ import org.junit.runner.RunWith; @RunWithLooper public class ExpandHelperTest extends SysuiTestCase { + @Rule + public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); private ExpandableNotificationRow mRow; private ExpandHelper mExpandHelper; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt index 0b2da8bfa6492..0cfca614a256a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt @@ -24,6 +24,7 @@ import android.util.Pair import android.view.Gravity import android.view.View import android.widget.FrameLayout +import androidx.core.animation.AnimatorTestRule import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlags @@ -38,6 +39,7 @@ import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock @@ -50,6 +52,7 @@ import org.mockito.MockitoAnnotations class SystemEventChipAnimationControllerTest : SysuiTestCase() { private lateinit var controller: SystemEventChipAnimationController + @get:Rule val animatorTestRule = AnimatorTestRule() @Mock private lateinit var sbWindowController: StatusBarWindowController @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider diff --git a/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt new file mode 100644 index 0000000000000..026372f64e2cd --- /dev/null +++ b/packages/SystemUI/tests/utils/src/androidx/core/animation/AndroidXAnimatorIsolationRule.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.core.animation + +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement + +class AndroidXAnimatorIsolationRule : TestRule { + + private class TestAnimationHandler : AnimationHandler(null) { + override fun addAnimationFrameCallback(callback: AnimationFrameCallback?) = doFail() + override fun removeCallback(callback: AnimationFrameCallback?) = doFail() + override fun onAnimationFrame(frameTime: Long) = doFail() + override fun setFrameDelay(frameDelay: Long) = doFail() + override fun getFrameDelay(): Long = doFail() + } + + override fun apply(base: Statement, description: Description): Statement { + return object : Statement() { + @Throws(Throwable::class) + override fun evaluate() { + AnimationHandler.setTestHandler(testHandler) + try { + base.evaluate() + } finally { + AnimationHandler.setTestHandler(null) + } + } + } + } + + companion object { + private val testHandler = TestAnimationHandler() + private fun doFail(): Nothing = + error( + "Test's animations are not isolated! " + + "Did you forget to add an AnimatorTestRule to your test class?" + ) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index de177168e20f0..28b7d4171df1d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -33,6 +33,7 @@ import android.testing.TestWithLooperRule; import android.testing.TestableLooper; import android.util.Log; +import androidx.core.animation.AndroidXAnimatorIsolationRule; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.UiDevice; @@ -52,6 +53,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialogManager; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; +import org.junit.ClassRule; import org.junit.Rule; import org.mockito.Mockito; @@ -69,6 +71,12 @@ public abstract class SysuiTestCase { private static final String TAG = "SysuiTestCase"; private Handler mHandler; + + // set the lowest order so it's the outermost rule + @ClassRule(order = Integer.MIN_VALUE) + public static AndroidXAnimatorIsolationRule mAndroidXAnimatorIsolationRule = + new AndroidXAnimatorIsolationRule(); + @Rule public SysuiTestableContext mContext = new SysuiTestableContext( InstrumentationRegistry.getContext(), getLeakCheck());