From 1f889a05b66ca1c37ecac6484b719f65d82558ed Mon Sep 17 00:00:00 2001 From: omarmt Date: Fri, 20 Jan 2023 16:43:40 +0000 Subject: [PATCH 1/2] Add a flag for predictive back animation in QS Dialog in Flags.kt. Add an AnimationFeatureFlags interface to access flags from AnimationLib module Test: m -j Bug: 265639042 Change-Id: Ie6456b04b02601e285ee08f70a1af7a038557065 --- .../systemui/animation/AnimationFeatureFlags.kt | 6 ++++++ .../src/com/android/systemui/flags/Flags.kt | 11 +++++++---- .../CentralSurfacesDependenciesModule.java | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt new file mode 100644 index 0000000000000..1c9dabbb0e075 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt @@ -0,0 +1,6 @@ +package com.android.systemui.animation + +interface AnimationFeatureFlags { + val isPredictiveBackQsDialogAnim: Boolean + get() = false +} diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index d3ea87a61a7da..adea0b1f566ca 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -65,8 +65,7 @@ object Flags { val FSI_ON_DND_UPDATE = unreleasedFlag(259130119, "fsi_on_dnd_update", teamfood = true) // TODO(b/265804648): Tracking Bug - @JvmField - val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi") + @JvmField val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi") // TODO(b/254512538): Tracking Bug val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply", teamfood = true) @@ -474,6 +473,11 @@ object Flags { val WM_SHADE_ANIMATE_BACK_GESTURE = unreleasedFlag(1208, "persist.wm.debug.shade_animate_back_gesture", teamfood = true) + // TODO(b/265639042): Tracking Bug + @JvmField + val WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM = + unreleasedFlag(1209, "persist.wm.debug.predictive_back_qs_dialog_anim", teamfood = true) + // 1300 - screenshots // TODO(b/254513155): Tracking Bug @JvmField @@ -564,6 +568,5 @@ object Flags { // 2600 - keyboard shortcut // TODO(b/259352579): Tracking Bug - @JvmField - val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout") + @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout") } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 9a65e342478e4..00852e23bf520 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -25,12 +25,15 @@ import android.util.Log; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.animation.AnimationFeatureFlags; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpHandler; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.plugins.ActivityStarter; @@ -305,4 +308,17 @@ public interface CentralSurfacesDependenciesModule { }; return new DialogLaunchAnimator(callback, interactionJankMonitor); } + + /** + */ + @Provides + @SysUISingleton + static AnimationFeatureFlags provideAnimationFeatureFlags(FeatureFlags featureFlags) { + return new AnimationFeatureFlags() { + @Override + public boolean isPredictiveBackQsDialogAnim() { + return featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_QS_DIALOG_ANIM); + } + }; + } } From 6a67abb2f81f22acc33bf2d9f866171c293bc214 Mon Sep 17 00:00:00 2001 From: omarmt Date: Wed, 18 Jan 2023 14:39:11 +0000 Subject: [PATCH 2/2] Add Predictive Back animation from Dialog to QS tile Test: atest BackTransformationTest Test: atest BackAnimationSpecTest Test: atest OnBackAnimationCallbackExtensionTest Bug: 238475317 Change-Id: I95bbeb3608906aa8f79177e62d94bafcf99b060c --- .../animation/DialogLaunchAnimator.kt | 59 +++++++++++-- .../animation/back/BackAnimationSpec.kt | 73 ++++++++++++++++ .../back/BackAnimationSpecForSysUi.kt | 75 ++++++++++++++++ .../animation/back/BackTransformation.kt | 39 +++++++++ .../back/OnBackAnimationCallbackExtension.kt | 66 ++++++++++++++ .../com/android/systemui/util/Dimension.kt | 32 +++++++ .../CentralSurfacesDependenciesModule.java | 5 +- .../animation/back/BackAnimationSpecTest.kt | 87 +++++++++++++++++++ .../animation/back/BackTransformationTest.kt | 80 +++++++++++++++++ .../OnBackAnimationCallbackExtensionTest.kt | 63 ++++++++++++++ .../animation/FakeDialogLaunchAnimator.kt | 18 ++-- 11 files changed, 580 insertions(+), 17 deletions(-) create mode 100644 packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt create mode 100644 packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt create mode 100644 packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt create mode 100644 packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt create mode 100644 packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index a3ed0856c60ae..e91a6710ace5f 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -33,8 +33,13 @@ import android.view.WindowInsets import android.view.WindowManager import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS import android.widget.FrameLayout +import android.window.OnBackInvokedDispatcher import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CujType +import com.android.systemui.animation.back.BackAnimationSpec +import com.android.systemui.animation.back.applyTo +import com.android.systemui.animation.back.floatingSystemSurfacesForSysUi +import com.android.systemui.animation.back.onBackAnimationCallbackFrom import kotlin.math.roundToInt private const val TAG = "DialogLaunchAnimator" @@ -55,8 +60,9 @@ class DialogLaunchAnimator constructor( private val callback: Callback, private val interactionJankMonitor: InteractionJankMonitor, + private val featureFlags: AnimationFeatureFlags, private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS), - private val isForTesting: Boolean = false + private val isForTesting: Boolean = false, ) { private companion object { private val TIMINGS = ActivityLaunchAnimator.TIMINGS @@ -273,15 +279,16 @@ constructor( val animatedDialog = AnimatedDialog( - launchAnimator, - callback, - interactionJankMonitor, - controller, + launchAnimator = launchAnimator, + callback = callback, + interactionJankMonitor = interactionJankMonitor, + controller = controller, onDialogDismissed = { openedDialogs.remove(it) }, dialog = dialog, - animateBackgroundBoundsChange, - animatedParent, - isForTesting, + animateBackgroundBoundsChange = animateBackgroundBoundsChange, + parentAnimatedDialog = animatedParent, + forceDisableSynchronization = isForTesting, + featureFlags = featureFlags, ) openedDialogs.add(animatedDialog) @@ -517,6 +524,7 @@ private class AnimatedDialog( * Whether synchronization should be disabled, which can be useful if we are running in a test. */ private val forceDisableSynchronization: Boolean, + private val featureFlags: AnimationFeatureFlags, ) { /** * The DecorView of this dialog window. @@ -778,12 +786,45 @@ private class AnimatedDialog( // the dialog. dialog.setDismissOverride(this::onDialogDismissed) + if (featureFlags.isPredictiveBackQsDialogAnim) { + // TODO(b/265923095) Improve animations for QS dialogs on configuration change + registerOnBackInvokedCallback(targetView = dialogContentWithBackground) + } + // Show the dialog. dialog.show() - moveSourceDrawingToDialog() } + private fun registerOnBackInvokedCallback(targetView: View) { + val metrics = targetView.resources.displayMetrics + + val onBackAnimationCallback = + onBackAnimationCallbackFrom( + backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(metrics), + displayMetrics = metrics, // TODO(b/265060720): We could remove this + onBackProgressed = { backTransformation -> backTransformation.applyTo(targetView) }, + onBackInvoked = { dialog.dismiss() }, + ) + + val dispatcher = dialog.onBackInvokedDispatcher + targetView.addOnAttachStateChangeListener( + object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) { + dispatcher.registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, + onBackAnimationCallback + ) + } + + override fun onViewDetachedFromWindow(v: View) { + targetView.removeOnAttachStateChangeListener(this) + dispatcher.unregisterOnBackInvokedCallback(onBackAnimationCallback) + } + } + ) + } + private fun moveSourceDrawingToDialog() { if (decorView.viewRootImpl == null) { // Make sure that we have access to the dialog view root to move the drawing to the diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt new file mode 100644 index 0000000000000..f3d8b17824865 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpec.kt @@ -0,0 +1,73 @@ +/* + * 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 com.android.systemui.animation.back + +import android.util.DisplayMetrics +import android.view.animation.Interpolator +import android.window.BackEvent +import com.android.systemui.animation.Interpolators +import com.android.systemui.util.dpToPx + +/** Used to convert [BackEvent] into a [BackTransformation]. */ +fun interface BackAnimationSpec { + + /** Computes transformation based on a [backEvent] and sets it to [result]. */ + fun getBackTransformation( + backEvent: BackEvent, + progressY: Float, // TODO(b/265060720): Remove progressY. Could be retrieved from backEvent + result: BackTransformation, + ) + + companion object +} + +/** Create a [BackAnimationSpec] from [displayMetrics] and design specs. */ +fun BackAnimationSpec.Companion.createFloatingSurfaceAnimationSpec( + displayMetrics: DisplayMetrics, + maxMarginXdp: Float, + maxMarginYdp: Float, + minScale: Float, + translateXEasing: Interpolator = Interpolators.STANDARD_DECELERATE, + translateYEasing: Interpolator = Interpolators.LINEAR, + scaleEasing: Interpolator = Interpolators.STANDARD_DECELERATE, +): BackAnimationSpec { + val screenWidthPx = displayMetrics.widthPixels + val screenHeightPx = displayMetrics.heightPixels + + val maxMarginXPx = maxMarginXdp.dpToPx(displayMetrics) + val maxMarginYPx = maxMarginYdp.dpToPx(displayMetrics) + val maxTranslationXByScale = (screenWidthPx - screenWidthPx * minScale) / 2 + val maxTranslationX = maxTranslationXByScale - maxMarginXPx + val maxTranslationYByScale = (screenHeightPx - screenHeightPx * minScale) / 2 + val maxTranslationY = maxTranslationYByScale - maxMarginYPx + val minScaleReversed = 1f - minScale + + return BackAnimationSpec { backEvent, progressY, result -> + val direction = if (backEvent.swipeEdge == BackEvent.EDGE_LEFT) 1 else -1 + val progressX = backEvent.progress + + val ratioTranslateX = translateXEasing.getInterpolation(progressX) + val ratioTranslateY = translateYEasing.getInterpolation(progressY) + val ratioScale = scaleEasing.getInterpolation(progressX) + + result.apply { + translateX = ratioTranslateX * direction * maxTranslationX + translateY = ratioTranslateY * maxTranslationY + scale = 1f - (ratioScale * minScaleReversed) + } + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt new file mode 100644 index 0000000000000..c6b70736bc670 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackAnimationSpecForSysUi.kt @@ -0,0 +1,75 @@ +/* + * 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 com.android.systemui.animation.back + +import android.util.DisplayMetrics + +/** + * SysUI transitions - Dismiss app (ST1) Return to launching surface or place of origin + * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-1-dismiss-app + */ +fun BackAnimationSpec.Companion.dismissAppForSysUi( + displayMetrics: DisplayMetrics, +): BackAnimationSpec = + BackAnimationSpec.createFloatingSurfaceAnimationSpec( + displayMetrics = displayMetrics, + maxMarginXdp = 8f, + maxMarginYdp = 8f, + minScale = 0.8f, + ) + +/** + * SysUI transitions - Cross task (ST2) Return to previous task/app, keeping the current one open + * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-2-cross-task + */ +fun BackAnimationSpec.Companion.crossTaskForSysUi( + displayMetrics: DisplayMetrics, +): BackAnimationSpec = + BackAnimationSpec.createFloatingSurfaceAnimationSpec( + displayMetrics = displayMetrics, + maxMarginXdp = 8f, + maxMarginYdp = 8f, + minScale = 0.8f, + ) + +/** + * SysUI transitions - Inner area dismiss (ST3) Dismiss non-detachable surface + * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-3-inner-area-dismiss + */ +fun BackAnimationSpec.Companion.innerAreaDismissForSysUi( + displayMetrics: DisplayMetrics, +): BackAnimationSpec = + BackAnimationSpec.createFloatingSurfaceAnimationSpec( + displayMetrics = displayMetrics, + maxMarginXdp = 0f, + maxMarginYdp = 0f, + minScale = 0.9f, + ) + +/** + * SysUI transitions - Floating system surfaces (ST4) + * https://carbon.googleplex.com/predictive-back-for-apps/pages/st-4-floating-system-surfaces + */ +fun BackAnimationSpec.Companion.floatingSystemSurfacesForSysUi( + displayMetrics: DisplayMetrics, +): BackAnimationSpec = + BackAnimationSpec.createFloatingSurfaceAnimationSpec( + displayMetrics = displayMetrics, + maxMarginXdp = 8f, + maxMarginYdp = 8f, + minScale = 0.8f, + ) diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt new file mode 100644 index 0000000000000..49d1fb423d2b2 --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/BackTransformation.kt @@ -0,0 +1,39 @@ +/* + * 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 com.android.systemui.animation.back + +import android.view.View + +/** + * This object that represents the transformation to apply to the target. The properties of this + * object are mutable for performance reasons (avoid recreating this object) + */ +data class BackTransformation( + var translateX: Float = Float.NaN, + var translateY: Float = Float.NaN, + var scale: Float = Float.NaN, +) + +/** Apply the transformation to the [targetView] */ +fun BackTransformation.applyTo(targetView: View) { + if (translateX.isFinite()) targetView.translationX = translateX + if (translateY.isFinite()) targetView.translationY = translateY + if (scale.isFinite()) { + targetView.scaleX = scale + targetView.scaleY = scale + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt new file mode 100644 index 0000000000000..33d14b14c702e --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtension.kt @@ -0,0 +1,66 @@ +/* + * 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 com.android.systemui.animation.back + +import android.util.DisplayMetrics +import android.window.BackEvent +import android.window.OnBackAnimationCallback + +/** + * Generates an [OnBackAnimationCallback] given a [backAnimationSpec]. [onBackProgressed] will be + * called on each update passing the current [BackTransformation]. + * + * Optionally, you can specify [onBackStarted], [onBackInvoked], and [onBackCancelled] callbacks. + */ +fun onBackAnimationCallbackFrom( + backAnimationSpec: BackAnimationSpec, + displayMetrics: DisplayMetrics, // TODO(b/265060720): We could remove this + onBackProgressed: (BackTransformation) -> Unit, + onBackStarted: (BackEvent) -> Unit = {}, + onBackInvoked: () -> Unit = {}, + onBackCancelled: () -> Unit = {}, +): OnBackAnimationCallback { + return object : OnBackAnimationCallback { + private var initialY = 0f + private val lastTransformation = BackTransformation() + + override fun onBackStarted(backEvent: BackEvent) { + initialY = backEvent.touchY + onBackStarted(backEvent) + } + + override fun onBackProgressed(backEvent: BackEvent) { + val progressY = (backEvent.touchY - initialY) / displayMetrics.heightPixels + + backAnimationSpec.getBackTransformation( + backEvent = backEvent, + progressY = progressY, + result = lastTransformation, + ) + + onBackProgressed(lastTransformation) + } + + override fun onBackInvoked() { + onBackInvoked() + } + + override fun onBackCancelled() { + onBackCancelled() + } + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt b/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt new file mode 100644 index 0000000000000..4bc9972dd50fe --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/util/Dimension.kt @@ -0,0 +1,32 @@ +/* + * 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 com.android.systemui.util + +import android.content.Context +import android.content.res.Resources +import android.util.DisplayMetrics +import android.util.TypedValue + +/** Convert [this] number of dps to device pixels. */ +fun Number.dpToPx(context: Context): Float = dpToPx(resources = context.resources) + +/** Convert [this] number of dps to device pixels. */ +fun Number.dpToPx(resources: Resources): Float = dpToPx(displayMetrics = resources.displayMetrics) + +/** Convert [this] number of dps to device pixels. */ +fun Number.dpToPx(displayMetrics: DisplayMetrics): Float = + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, toFloat(), displayMetrics) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 00852e23bf520..098c6175a93e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -284,7 +284,8 @@ public interface CentralSurfacesDependenciesModule { static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager, KeyguardStateController keyguardStateController, Lazy alternateBouncerInteractor, - InteractionJankMonitor interactionJankMonitor) { + InteractionJankMonitor interactionJankMonitor, + AnimationFeatureFlags animationFeatureFlags) { DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() { @Override public boolean isDreaming() { @@ -306,7 +307,7 @@ public interface CentralSurfacesDependenciesModule { return alternateBouncerInteractor.get().canShowAlternateBouncerForFingerprint(); } }; - return new DialogLaunchAnimator(callback, interactionJankMonitor); + return new DialogLaunchAnimator(callback, interactionJankMonitor, animationFeatureFlags); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt new file mode 100644 index 0000000000000..3bdbf9789f5ce --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt @@ -0,0 +1,87 @@ +package com.android.systemui.animation.back + +import android.util.DisplayMetrics +import android.window.BackEvent +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +private data class BackInput(val progressX: Float, val progressY: Float, val edge: Int) + +@SmallTest +@RunWith(JUnit4::class) +class BackAnimationSpecTest : SysuiTestCase() { + private var displayMetrics = + DisplayMetrics().apply { + widthPixels = 100 + heightPixels = 200 + density = 3f + } + + @Test + fun sysUi_floatingSystemSurfaces_animationValues() { + val maxX = 14.0f + val maxY = 4.0f + val minScale = 0.8f + + val backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(displayMetrics) + + assertBackTransformation( + backAnimationSpec = backAnimationSpec, + backInput = BackInput(progressX = 0f, progressY = 0f, edge = BackEvent.EDGE_LEFT), + expected = BackTransformation(translateX = 0f, translateY = 0f, scale = 1f), + ) + assertBackTransformation( + backAnimationSpec = backAnimationSpec, + backInput = BackInput(progressX = 1f, progressY = 0f, edge = BackEvent.EDGE_LEFT), + expected = BackTransformation(translateX = -maxX, translateY = 0f, scale = minScale), + ) + assertBackTransformation( + backAnimationSpec = backAnimationSpec, + backInput = BackInput(progressX = 1f, progressY = 0f, edge = BackEvent.EDGE_RIGHT), + expected = BackTransformation(translateX = maxX, translateY = 0f, scale = minScale), + ) + assertBackTransformation( + backAnimationSpec = backAnimationSpec, + backInput = BackInput(progressX = 1f, progressY = 1f, edge = BackEvent.EDGE_LEFT), + expected = BackTransformation(translateX = -maxX, translateY = -maxY, scale = minScale), + ) + assertBackTransformation( + backAnimationSpec = backAnimationSpec, + backInput = BackInput(progressX = 0f, progressY = 1f, edge = BackEvent.EDGE_LEFT), + expected = BackTransformation(translateX = 0f, translateY = -maxY, scale = 1f), + ) + assertBackTransformation( + backAnimationSpec = backAnimationSpec, + backInput = BackInput(progressX = 0f, progressY = -1f, edge = BackEvent.EDGE_LEFT), + expected = BackTransformation(translateX = 0f, translateY = maxY, scale = 1f), + ) + } +} + +private fun assertBackTransformation( + backAnimationSpec: BackAnimationSpec, + backInput: BackInput, + expected: BackTransformation, +) { + val actual = BackTransformation() + backAnimationSpec.getBackTransformation( + backEvent = + BackEvent( + /* touchX = */ 0f, + /* touchY = */ 0f, + /* progress = */ backInput.progressX, + /* swipeEdge = */ backInput.edge, + ), + progressY = backInput.progressY, + result = actual + ) + + val tolerance = 0f + assertThat(actual.translateX).isWithin(tolerance).of(expected.translateX) + assertThat(actual.translateY).isWithin(tolerance).of(expected.translateY) + assertThat(actual.scale).isWithin(tolerance).of(expected.scale) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt new file mode 100644 index 0000000000000..190b3d25d16bb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt @@ -0,0 +1,80 @@ +package com.android.systemui.animation.back + +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions + +@SmallTest +@RunWith(JUnit4::class) +class BackTransformationTest : SysuiTestCase() { + private val targetView: View = mock() + + @Test + fun defaultValue_noTransformation() { + val transformation = BackTransformation() + + assertThat(transformation.translateX).isNaN() + assertThat(transformation.translateY).isNaN() + assertThat(transformation.scale).isNaN() + } + + @Test + fun applyTo_targetView_translateX_Y_Scale() { + val transformation = BackTransformation(translateX = 0f, translateY = 0f, scale = 1f) + + transformation.applyTo(targetView = targetView) + + verify(targetView).translationX = 0f + verify(targetView).translationY = 0f + verify(targetView).scaleX = 1f + verify(targetView).scaleY = 1f + verifyNoMoreInteractions(targetView) + } + + @Test + fun applyTo_targetView_translateX() { + val transformation = BackTransformation(translateX = 1f) + + transformation.applyTo(targetView = targetView) + + verify(targetView).translationX = 1f + verifyNoMoreInteractions(targetView) + } + + @Test + fun applyTo_targetView_translateY() { + val transformation = BackTransformation(translateY = 2f) + + transformation.applyTo(targetView = targetView) + + verify(targetView).translationY = 2f + verifyNoMoreInteractions(targetView) + } + + @Test + fun applyTo_targetView_scale() { + val transformation = BackTransformation(scale = 3f) + + transformation.applyTo(targetView = targetView) + + verify(targetView).scaleX = 3f + verify(targetView).scaleY = 3f + verifyNoMoreInteractions(targetView) + } + + @Test + fun applyTo_targetView_noTransformation() { + val transformation = BackTransformation() + + transformation.applyTo(targetView = targetView) + + verifyNoMoreInteractions(targetView) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt new file mode 100644 index 0000000000000..921f9a8fc7d6f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt @@ -0,0 +1,63 @@ +package com.android.systemui.animation.back + +import android.util.DisplayMetrics +import android.window.BackEvent +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mockito.verify + +@SmallTest +@RunWith(JUnit4::class) +class OnBackAnimationCallbackExtensionTest : SysuiTestCase() { + private val onBackProgress: (BackTransformation) -> Unit = mock() + private val onBackStart: (BackEvent) -> Unit = mock() + private val onBackInvoke: () -> Unit = mock() + private val onBackCancel: () -> Unit = mock() + + private val displayMetrics = + DisplayMetrics().apply { + widthPixels = 100 + heightPixels = 100 + density = 1f + } + + private val onBackAnimationCallback = + onBackAnimationCallbackFrom( + backAnimationSpec = BackAnimationSpec.floatingSystemSurfacesForSysUi(displayMetrics), + displayMetrics = displayMetrics, + onBackProgressed = onBackProgress, + onBackStarted = onBackStart, + onBackInvoked = onBackInvoke, + onBackCancelled = onBackCancel, + ) + + @Test + fun onBackProgressed_shouldInvoke_onBackProgress() { + val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT) + onBackAnimationCallback.onBackStarted(backEvent) + + onBackAnimationCallback.onBackProgressed(backEvent) + + verify(onBackProgress).invoke(BackTransformation(0f, 0f, 1f)) + } + + @Test + fun onBackStarted_shouldInvoke_onBackStart() { + val backEvent = BackEvent(0f, 0f, 0f, BackEvent.EDGE_LEFT) + + onBackAnimationCallback.onBackStarted(backEvent) + + verify(onBackStart).invoke(backEvent) + } + + @Test + fun onBackInvoked_shouldInvoke_onBackInvoke() { + onBackAnimationCallback.onBackInvoked() + + verify(onBackInvoke).invoke() + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt index 990db77463f61..f723a9e57e728 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt @@ -23,14 +23,20 @@ fun fakeDialogLaunchAnimator( isUnlocked: Boolean = true, isShowingAlternateAuthOnUnlock: Boolean = false, interactionJankMonitor: InteractionJankMonitor = mock(InteractionJankMonitor::class.java), + isPredictiveBackQsDialogAnim: Boolean = false, ): DialogLaunchAnimator { return DialogLaunchAnimator( - FakeCallback( - isUnlocked = isUnlocked, - isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock, - ), - interactionJankMonitor, - fakeLaunchAnimator(), + callback = + FakeCallback( + isUnlocked = isUnlocked, + isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock, + ), + interactionJankMonitor = interactionJankMonitor, + featureFlags = + object : AnimationFeatureFlags { + override val isPredictiveBackQsDialogAnim = isPredictiveBackQsDialogAnim + }, + launchAnimator = fakeLaunchAnimator(), isForTesting = true, ) }