Merge changes from topic "predback-qs-dialog" into tm-qpr-dev
* changes:
Add Predictive Back animation from Dialog to QS tile
Add a flag for predictive back animation in QS Dialog in Flags.kt. Add an AnimationFeatureFlags interface to access flags from AnimationLib module
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 0000000..1c9dabb
--- /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/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index a3ed085..e91a671 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.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 @@
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 @@
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 @@
* 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 @@
// 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 0000000..f3d8b17
--- /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 0000000..c6b7073
--- /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 0000000..49d1fb4
--- /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 0000000..33d14b1
--- /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 0000000..4bc9972
--- /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/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index b954331..0749bcf 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 @@
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)
@@ -483,6 +482,11 @@
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
@@ -573,6 +577,5 @@
// 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 9a65e34..098c617 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 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;
@@ -281,7 +284,8 @@
static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
KeyguardStateController keyguardStateController,
Lazy<AlternateBouncerInteractor> alternateBouncerInteractor,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ AnimationFeatureFlags animationFeatureFlags) {
DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() {
@Override
public boolean isDreaming() {
@@ -303,6 +307,19 @@
return alternateBouncerInteractor.get().canShowAlternateBouncerForFingerprint();
}
};
- return new DialogLaunchAnimator(callback, interactionJankMonitor);
+ return new DialogLaunchAnimator(callback, interactionJankMonitor, animationFeatureFlags);
+ }
+
+ /**
+ */
+ @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);
+ }
+ };
}
}
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 0000000..3bdbf97
--- /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 0000000..190b3d2
--- /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 0000000..921f9a8
--- /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 990db77..f723a9e5 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 @@
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,
)
}