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
This commit is contained in:
committed by
Android (Google) Code Review
commit
1b5fb5d809
@@ -0,0 +1,6 @@
|
||||
package com.android.systemui.animation
|
||||
|
||||
interface AnimationFeatureFlags {
|
||||
val isPredictiveBackQsDialogAnim: Boolean
|
||||
get() = false
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -483,6 +482,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
|
||||
@@ -573,6 +577,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")
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -281,7 +284,8 @@ public interface CentralSurfacesDependenciesModule {
|
||||
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 @@ public interface CentralSurfacesDependenciesModule {
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user