Scale recents on drag of tasks during dismiss.

Fix: 389083567
Test: TaplTestsQuickstep#testDismissCancel
Flag: com.android.launcher3.enable_expressive_dismiss_task_motion
Change-Id: Ibeb0a742cdfb4392c920deb64dd5bd4f221a9564
This commit is contained in:
Pat Manning
2025-02-12 15:04:33 +00:00
parent 849d204c71
commit 3b355f645b
3 changed files with 94 additions and 3 deletions
@@ -19,6 +19,7 @@ import android.content.Context
import android.view.MotionEvent
import androidx.dynamicanimation.animation.SpringAnimation
import com.android.app.animation.Interpolators.DECELERATE
import com.android.app.animation.Interpolators.LINEAR
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.R
import com.android.launcher3.Utilities.EDGE_NAV_BAR
@@ -29,6 +30,7 @@ import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.util.MSDLPlayerWrapper
import com.android.launcher3.util.TouchController
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskView
import com.google.android.msdl.data.model.MSDLToken
@@ -57,6 +59,7 @@ CONTAINER : RecentsViewContainer {
private var verticalFactor: Int = 0
private var hasDismissThresholdHapticRun = false
private var initialDisplacement: Float = 0f
private var recentsScaleAnimation: SpringAnimation? = null
private fun canInterceptTouch(ev: MotionEvent): Boolean =
when {
@@ -98,6 +101,7 @@ CONTAINER : RecentsViewContainer {
private fun onActionDown(ev: MotionEvent): Boolean {
springAnimation?.cancel()
recentsScaleAnimation?.cancel()
if (!canInterceptTouch(ev)) {
return false
}
@@ -162,6 +166,8 @@ CONTAINER : RecentsViewContainer {
}
recentsView.redrawLiveTile()
}
val dismissFraction = displacement / (dismissLength * verticalFactor).toFloat()
RECENTS_SCALE_PROPERTY.setValue(recentsView, getRecentsScale(dismissFraction))
playDismissThresholdHaptic(displacement)
return true
}
@@ -216,6 +222,10 @@ CONTAINER : RecentsViewContainer {
if (isDismissing) (dismissLength * verticalFactor).toFloat() else 0f
)
}
recentsScaleAnimation =
recentsView.animateRecentsScale(RECENTS_SCALE_DEFAULT).addEndListener { _, _, _, _ ->
recentsScaleAnimation = null
}
}
// Returns if the current task being dragged is towards "positive" (e.g. dismissal).
@@ -230,8 +240,54 @@ CONTAINER : RecentsViewContainer {
springAnimation = null
}
private fun getRecentsScale(dismissFraction: Float): Float {
return when {
// Do not scale recents when dragging below origin.
dismissFraction <= 0 -> {
RECENTS_SCALE_DEFAULT
}
// Initially scale recents as the drag begins, up to the first threshold.
dismissFraction < RECENTS_SCALE_FIRST_THRESHOLD_FRACTION -> {
mapToRange(
dismissFraction,
0f,
RECENTS_SCALE_FIRST_THRESHOLD_FRACTION,
RECENTS_SCALE_DEFAULT,
RECENTS_SCALE_ON_DISMISS_CANCEL,
LINEAR,
)
}
// Keep scale consistent until dragging to the dismiss threshold.
dismissFraction < RECENTS_SCALE_DISMISS_THRESHOLD_FRACTION -> {
RECENTS_SCALE_ON_DISMISS_CANCEL
}
// Scale beyond the dismiss threshold again, to indicate dismiss will occur on release.
dismissFraction < RECENTS_SCALE_SECOND_THRESHOLD_FRACTION -> {
mapToRange(
dismissFraction,
RECENTS_SCALE_DISMISS_THRESHOLD_FRACTION,
RECENTS_SCALE_SECOND_THRESHOLD_FRACTION,
RECENTS_SCALE_ON_DISMISS_CANCEL,
RECENTS_SCALE_ON_DISMISS_SUCCESS,
LINEAR,
)
}
// Keep scale beyond the dismiss threshold scaling consistent.
else -> {
RECENTS_SCALE_ON_DISMISS_SUCCESS
}
}
}
companion object {
private const val DISMISS_THRESHOLD_FRACTION = 0.5f
private const val DISMISS_THRESHOLD_HAPTIC_RANGE = 10f
private const val RECENTS_SCALE_ON_DISMISS_CANCEL = 0.9875f
private const val RECENTS_SCALE_ON_DISMISS_SUCCESS = 0.975f
private const val RECENTS_SCALE_DEFAULT = 1f
private const val RECENTS_SCALE_FIRST_THRESHOLD_FRACTION = 0.2f
private const val RECENTS_SCALE_DISMISS_THRESHOLD_FRACTION = 0.5f
private const val RECENTS_SCALE_SECOND_THRESHOLD_FRACTION = 0.575f
}
}
@@ -6950,6 +6950,13 @@ public abstract class RecentsView<
isDismissing, detector, dismissLength, onEndRunnable);
}
/**
* Animates RecentsView's scale to the provided value, using spring animations.
*/
public SpringAnimation animateRecentsScale(float scale) {
return mUtils.animateRecentsScale(scale);
}
public interface TaskLaunchListener {
void onTaskLaunched();
}
@@ -35,6 +35,7 @@ import com.android.launcher3.util.MSDLPlayerWrapper
import com.android.quickstep.util.GroupTask
import com.android.quickstep.util.TaskGridNavHelper
import com.android.quickstep.util.isExternalDisplay
import com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY
import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.android.msdl.data.model.MSDLToken
@@ -362,7 +363,6 @@ class RecentsViewUtils(private val recentsView: RecentsView<*, *>) {
addNeighboringSpringAnimationsForDismissCancel(
draggedTaskView,
draggedTaskViewSpringAnimation,
recentsView.pageCount,
)
}
return draggedTaskViewSpringAnimation
@@ -371,7 +371,6 @@ class RecentsViewUtils(private val recentsView: RecentsView<*, *>) {
private fun addNeighboringSpringAnimationsForDismissCancel(
draggedTaskView: TaskView,
draggedTaskViewSpringAnimation: SpringAnimation,
taskCount: Int,
) {
// Empty spring animation exists for conditional start, and to drive neighboring springs.
val neighborsToSettle =
@@ -559,10 +558,39 @@ class RecentsViewUtils(private val recentsView: RecentsView<*, *>) {
)
}
/** Animates RecentsView's scale to the provided value, using spring animations. */
fun animateRecentsScale(scale: Float): SpringAnimation {
val resourceProvider = DynamicResource.provider(recentsView.mContainer)
val dampingRatio = resourceProvider.getFloat(R.dimen.swipe_up_rect_scale_damping_ratio)
val stiffness = resourceProvider.getFloat(R.dimen.swipe_up_rect_scale_stiffness)
// Spring which sets the Recents scale on update. This is needed, as the SpringAnimation
// struggles to animate small values like changing recents scale from 0.9 to 1. So
// we animate over a larger range (e.g. 900 to 1000) and convert back to the required value.
// (This is instead of converting RECENTS_SCALE_PROPERTY to a FloatPropertyCompat and
// animating it directly via springs.)
val initialRecentsScaleSpringValue =
RECENTS_SCALE_SPRING_MULTIPLIER * RECENTS_SCALE_PROPERTY.get(recentsView)
return SpringAnimation(FloatValueHolder(initialRecentsScaleSpringValue))
.setSpring(
SpringForce(initialRecentsScaleSpringValue)
.setDampingRatio(dampingRatio)
.setStiffness(stiffness)
)
.addUpdateListener { _, value, _ ->
RECENTS_SCALE_PROPERTY.setValue(
recentsView,
value / RECENTS_SCALE_SPRING_MULTIPLIER,
)
}
.apply { animateToFinalPosition(RECENTS_SCALE_SPRING_MULTIPLIER * scale) }
}
companion object {
val TEMP_RECT = Rect()
// The additional damping to apply to tasks further from the dismissed task.
const val ADDITIONAL_DISMISS_DAMPING_RATIO = 0.15f
private const val ADDITIONAL_DISMISS_DAMPING_RATIO = 0.15f
private const val RECENTS_SCALE_SPRING_MULTIPLIER = 1000f
}
}