Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +68 −13 Original line number Diff line number Diff line Loading @@ -22,13 +22,15 @@ import android.graphics.Rect import android.os.Bundle import android.os.IBinder import android.os.SystemClock import android.os.SystemProperties import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CLOSE import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import androidx.dynamicanimation.animation.SpringForce import com.android.internal.annotations.VisibleForTesting import com.android.internal.dynamicanimation.animation.SpringForce import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE import com.android.internal.jank.InteractionJankMonitor Loading Loading @@ -893,13 +895,10 @@ constructor( ) { private val positionSpringConfig = PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY ) PhysicsAnimator.SpringConfig(POSITION_SPRING_STIFFNESS, POSITION_SPRING_DAMPING_RATIO) private val sizeSpringConfig = PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY) PhysicsAnimator.SpringConfig(SIZE_SPRING_STIFFNESS, SIZE_SPRING_DAMPING_RATIO) /** * @return layers in order: Loading Loading @@ -929,7 +928,7 @@ constructor( finishTransaction.hide(homeLeash) // Setup freeform tasks before animation state.freeformTaskChanges.forEach { change -> val startScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE val startScale = FREEFORM_TASKS_INITIAL_SCALE val startX = change.endAbsBounds.left + change.endAbsBounds.width() * (1 - startScale) / 2 val startY = Loading Loading @@ -994,9 +993,22 @@ constructor( (animBounds.width() - startBounds.width()).toFloat() / (endBounds.width() - startBounds.width()) val animScale = startScale + animFraction * (1 - startScale) // Freeform animation starts 50% in the animation val freeformAnimFraction = max(animFraction - 0.5f, 0f) * 2f val freeformStartScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE // Freeform animation starts with freeform animation offset relative to the commit // animation and plays until the commit animation ends. For instance: // - if the freeform animation offset is `0.0` the freeform tasks animate alongside // - if the freeform animation offset is `0.6` the freeform tasks will // start animating at 60% fraction of the commit animation and will complete when // the commit animation fraction is 100%. // - if the freeform animation offset is `1.0` then freeform tasks will appear // without animation after commit animation finishes. val freeformAnimFraction = if (FREEFORM_TASKS_ANIM_OFFSET != 1f) { max(animFraction - FREEFORM_TASKS_ANIM_OFFSET, 0f) / (1f - FREEFORM_TASKS_ANIM_OFFSET) } else { 0f } val freeformStartScale = FREEFORM_TASKS_INITIAL_SCALE val freeformAnimScale = freeformStartScale + freeformAnimFraction * (1 - freeformStartScale) tx.apply { Loading Loading @@ -1032,10 +1044,53 @@ constructor( } companion object { /** The freeform tasks initial scale when committing the drag-to-desktop gesture. */ private val FREEFORM_TASKS_INITIAL_SCALE = propertyValue("freeform_tasks_initial_scale", scale = 100f, default = 0.9f) /** The freeform tasks animation offset relative to the whole animation duration. */ private val FREEFORM_TASKS_ANIM_OFFSET = propertyValue("freeform_tasks_anim_offset", scale = 100f, default = 0.5f) /** The spring force stiffness used to place the window into the final position. */ private val POSITION_SPRING_STIFFNESS = propertyValue("position_stiffness", default = SpringForce.STIFFNESS_LOW) /** The spring force damping ratio used to place the window into the final position. */ private val POSITION_SPRING_DAMPING_RATIO = propertyValue( "position_damping_ratio", scale = 100f, default = SpringForce.DAMPING_RATIO_LOW_BOUNCY ) /** The spring force stiffness used to resize the window into the final bounds. */ private val SIZE_SPRING_STIFFNESS = propertyValue("size_stiffness", default = SpringForce.STIFFNESS_LOW) /** The spring force damping ratio used to resize the window into the final bounds. */ private val SIZE_SPRING_DAMPING_RATIO = propertyValue( "size_damping_ratio", scale = 100f, default = SpringForce.DAMPING_RATIO_NO_BOUNCY ) /** Drag to desktop transition system properties group. */ @VisibleForTesting const val SYSTEM_PROPERTIES_GROUP = "persist.wm.debug.desktop_transitions.drag_to_desktop" /** * The initial scale of the freeform tasks in the animation to commit the drag-to-desktop * gesture. * Drag to desktop transition system property value with [name]. * * @param scale an optional scale to apply to the value read from the system property. * @param default a default value to return if the system property isn't set. */ private const val DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE = 0.9f @VisibleForTesting fun propertyValue(name: String, scale: Float = 1f, default: Float = 0f): Float = SystemProperties.getInt( /* key= */ "$SYSTEM_PROPERTIES_GROUP.$name", /* def= */ (default * scale).toInt() ) / scale } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +96 −2 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.app.WindowConfiguration.WindowingMode import android.graphics.PointF import android.os.IBinder import android.os.SystemProperties import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.SurfaceControl Loading @@ -16,6 +17,7 @@ import android.window.TransitionInfo import android.window.TransitionInfo.FLAG_IS_WALLPAPER import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.jank.InteractionJankMonitor import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase Loading @@ -29,19 +31,24 @@ import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import java.util.function.Supplier import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.MockitoSession import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyZeroInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness /** Tests of [DragToDesktopTransitionHandler]. */ @SmallTest Loading @@ -61,10 +68,12 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { private lateinit var defaultHandler: DragToDesktopTransitionHandler private lateinit var springHandler: SpringDragToDesktopTransitionHandler private lateinit var mockitoSession: MockitoSession @Before fun setUp() { defaultHandler = DefaultDragToDesktopTransitionHandler( defaultHandler = DefaultDragToDesktopTransitionHandler( context, transitions, taskDisplayAreaOrganizer, Loading @@ -72,7 +81,8 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { transactionSupplier, ) .apply { setSplitScreenController(splitScreenController) } springHandler = SpringDragToDesktopTransitionHandler( springHandler = SpringDragToDesktopTransitionHandler( context, transitions, taskDisplayAreaOrganizer, Loading @@ -80,6 +90,16 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { transactionSupplier, ) .apply { setSplitScreenController(splitScreenController) } mockitoSession = ExtendedMockito.mockitoSession() .strictness(Strictness.LENIENT) .mockStatic(SystemProperties::class.java) .startMocking() } @After fun tearDown() { mockitoSession.finishMocking() } @Test Loading Loading @@ -357,6 +377,77 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { verify(finishCallback).onTransitionFinished(null) } @Test fun propertyValue_returnsSystemPropertyValue() { val name = "property_name" val value = 10f whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt())) .thenReturn(value.toInt()) assertEquals( "Expects to return system properties stored value", /* expected= */ value, /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name) ) } @Test fun propertyValue_withScale_returnsScaledSystemPropertyValue() { val name = "property_name" val value = 10f val scale = 100f whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt())) .thenReturn(value.toInt()) assertEquals( "Expects to return scaled system properties stored value", /* expected= */ value / scale, /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale) ) } @Test fun propertyValue_notSet_returnsDefaultValue() { val name = "property_name" val defaultValue = 50f whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(defaultValue.toInt()))) .thenReturn(defaultValue.toInt()) assertEquals( "Expects to return the default value", /* expected= */ defaultValue, /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue( name, default = defaultValue ) ) } @Test fun propertyValue_withScaleNotSet_returnsDefaultValue() { val name = "property_name" val defaultValue = 0.5f val scale = 100f // Default value is multiplied when provided as a default value for [SystemProperties] val scaledDefault = (defaultValue * scale).toInt() whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(scaledDefault))) .thenReturn(scaledDefault) assertEquals( "Expects to return the default value", /* expected= */ defaultValue, /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue( name, default = defaultValue, scale = scale ) ) } private fun startDrag( handler: DragToDesktopTransitionHandler, task: RunningTaskInfo = createTask(), Loading Loading @@ -462,4 +553,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { ) } } private fun systemPropertiesKey(name: String) = "${SpringDragToDesktopTransitionHandler.SYSTEM_PROPERTIES_GROUP}.$name" } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +68 −13 Original line number Diff line number Diff line Loading @@ -22,13 +22,15 @@ import android.graphics.Rect import android.os.Bundle import android.os.IBinder import android.os.SystemClock import android.os.SystemProperties import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CLOSE import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import androidx.dynamicanimation.animation.SpringForce import com.android.internal.annotations.VisibleForTesting import com.android.internal.dynamicanimation.animation.SpringForce import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE import com.android.internal.jank.InteractionJankMonitor Loading Loading @@ -893,13 +895,10 @@ constructor( ) { private val positionSpringConfig = PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY ) PhysicsAnimator.SpringConfig(POSITION_SPRING_STIFFNESS, POSITION_SPRING_DAMPING_RATIO) private val sizeSpringConfig = PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY) PhysicsAnimator.SpringConfig(SIZE_SPRING_STIFFNESS, SIZE_SPRING_DAMPING_RATIO) /** * @return layers in order: Loading Loading @@ -929,7 +928,7 @@ constructor( finishTransaction.hide(homeLeash) // Setup freeform tasks before animation state.freeformTaskChanges.forEach { change -> val startScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE val startScale = FREEFORM_TASKS_INITIAL_SCALE val startX = change.endAbsBounds.left + change.endAbsBounds.width() * (1 - startScale) / 2 val startY = Loading Loading @@ -994,9 +993,22 @@ constructor( (animBounds.width() - startBounds.width()).toFloat() / (endBounds.width() - startBounds.width()) val animScale = startScale + animFraction * (1 - startScale) // Freeform animation starts 50% in the animation val freeformAnimFraction = max(animFraction - 0.5f, 0f) * 2f val freeformStartScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE // Freeform animation starts with freeform animation offset relative to the commit // animation and plays until the commit animation ends. For instance: // - if the freeform animation offset is `0.0` the freeform tasks animate alongside // - if the freeform animation offset is `0.6` the freeform tasks will // start animating at 60% fraction of the commit animation and will complete when // the commit animation fraction is 100%. // - if the freeform animation offset is `1.0` then freeform tasks will appear // without animation after commit animation finishes. val freeformAnimFraction = if (FREEFORM_TASKS_ANIM_OFFSET != 1f) { max(animFraction - FREEFORM_TASKS_ANIM_OFFSET, 0f) / (1f - FREEFORM_TASKS_ANIM_OFFSET) } else { 0f } val freeformStartScale = FREEFORM_TASKS_INITIAL_SCALE val freeformAnimScale = freeformStartScale + freeformAnimFraction * (1 - freeformStartScale) tx.apply { Loading Loading @@ -1032,10 +1044,53 @@ constructor( } companion object { /** The freeform tasks initial scale when committing the drag-to-desktop gesture. */ private val FREEFORM_TASKS_INITIAL_SCALE = propertyValue("freeform_tasks_initial_scale", scale = 100f, default = 0.9f) /** The freeform tasks animation offset relative to the whole animation duration. */ private val FREEFORM_TASKS_ANIM_OFFSET = propertyValue("freeform_tasks_anim_offset", scale = 100f, default = 0.5f) /** The spring force stiffness used to place the window into the final position. */ private val POSITION_SPRING_STIFFNESS = propertyValue("position_stiffness", default = SpringForce.STIFFNESS_LOW) /** The spring force damping ratio used to place the window into the final position. */ private val POSITION_SPRING_DAMPING_RATIO = propertyValue( "position_damping_ratio", scale = 100f, default = SpringForce.DAMPING_RATIO_LOW_BOUNCY ) /** The spring force stiffness used to resize the window into the final bounds. */ private val SIZE_SPRING_STIFFNESS = propertyValue("size_stiffness", default = SpringForce.STIFFNESS_LOW) /** The spring force damping ratio used to resize the window into the final bounds. */ private val SIZE_SPRING_DAMPING_RATIO = propertyValue( "size_damping_ratio", scale = 100f, default = SpringForce.DAMPING_RATIO_NO_BOUNCY ) /** Drag to desktop transition system properties group. */ @VisibleForTesting const val SYSTEM_PROPERTIES_GROUP = "persist.wm.debug.desktop_transitions.drag_to_desktop" /** * The initial scale of the freeform tasks in the animation to commit the drag-to-desktop * gesture. * Drag to desktop transition system property value with [name]. * * @param scale an optional scale to apply to the value read from the system property. * @param default a default value to return if the system property isn't set. */ private const val DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE = 0.9f @VisibleForTesting fun propertyValue(name: String, scale: Float = 1f, default: Float = 0f): Float = SystemProperties.getInt( /* key= */ "$SYSTEM_PROPERTIES_GROUP.$name", /* def= */ (default * scale).toInt() ) / scale } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +96 −2 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.app.WindowConfiguration.WindowingMode import android.graphics.PointF import android.os.IBinder import android.os.SystemProperties import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.SurfaceControl Loading @@ -16,6 +17,7 @@ import android.window.TransitionInfo import android.window.TransitionInfo.FLAG_IS_WALLPAPER import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.jank.InteractionJankMonitor import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase Loading @@ -29,19 +31,24 @@ import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import java.util.function.Supplier import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.MockitoSession import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyZeroInteractions import org.mockito.kotlin.whenever import org.mockito.quality.Strictness /** Tests of [DragToDesktopTransitionHandler]. */ @SmallTest Loading @@ -61,10 +68,12 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { private lateinit var defaultHandler: DragToDesktopTransitionHandler private lateinit var springHandler: SpringDragToDesktopTransitionHandler private lateinit var mockitoSession: MockitoSession @Before fun setUp() { defaultHandler = DefaultDragToDesktopTransitionHandler( defaultHandler = DefaultDragToDesktopTransitionHandler( context, transitions, taskDisplayAreaOrganizer, Loading @@ -72,7 +81,8 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { transactionSupplier, ) .apply { setSplitScreenController(splitScreenController) } springHandler = SpringDragToDesktopTransitionHandler( springHandler = SpringDragToDesktopTransitionHandler( context, transitions, taskDisplayAreaOrganizer, Loading @@ -80,6 +90,16 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { transactionSupplier, ) .apply { setSplitScreenController(splitScreenController) } mockitoSession = ExtendedMockito.mockitoSession() .strictness(Strictness.LENIENT) .mockStatic(SystemProperties::class.java) .startMocking() } @After fun tearDown() { mockitoSession.finishMocking() } @Test Loading Loading @@ -357,6 +377,77 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { verify(finishCallback).onTransitionFinished(null) } @Test fun propertyValue_returnsSystemPropertyValue() { val name = "property_name" val value = 10f whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt())) .thenReturn(value.toInt()) assertEquals( "Expects to return system properties stored value", /* expected= */ value, /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name) ) } @Test fun propertyValue_withScale_returnsScaledSystemPropertyValue() { val name = "property_name" val value = 10f val scale = 100f whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt())) .thenReturn(value.toInt()) assertEquals( "Expects to return scaled system properties stored value", /* expected= */ value / scale, /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale) ) } @Test fun propertyValue_notSet_returnsDefaultValue() { val name = "property_name" val defaultValue = 50f whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(defaultValue.toInt()))) .thenReturn(defaultValue.toInt()) assertEquals( "Expects to return the default value", /* expected= */ defaultValue, /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue( name, default = defaultValue ) ) } @Test fun propertyValue_withScaleNotSet_returnsDefaultValue() { val name = "property_name" val defaultValue = 0.5f val scale = 100f // Default value is multiplied when provided as a default value for [SystemProperties] val scaledDefault = (defaultValue * scale).toInt() whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(scaledDefault))) .thenReturn(scaledDefault) assertEquals( "Expects to return the default value", /* expected= */ defaultValue, /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue( name, default = defaultValue, scale = scale ) ) } private fun startDrag( handler: DragToDesktopTransitionHandler, task: RunningTaskInfo = createTask(), Loading Loading @@ -462,4 +553,7 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { ) } } private fun systemPropertiesKey(name: String) = "${SpringDragToDesktopTransitionHandler.SYSTEM_PROPERTIES_GROUP}.$name" }