diff options
3 files changed, 117 insertions, 14 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index c5bc9ebb0594..35c1e8c2a047 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -3521,7 +3521,14 @@ public class BubbleStackView extends FrameLayout */ void onVerticalOffsetChanged(int offset) { // adjust dismiss view vertical position, so that it is still visible to the user - mDismissView.setPadding(/* left = */ 0, /* top = */ 0, /* right = */ 0, offset); + ViewGroup.LayoutParams lp = mDismissView.getLayoutParams(); + if (lp instanceof FrameLayout.LayoutParams) { + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) lp; + layoutParams.bottomMargin = offset; + mDismissView.setLayoutParams(layoutParams); + } + mMagneticTarget.setScreenVerticalOffset(offset); + mMagneticTarget.updateLocationOnScreen(); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt index aac1d0626d30..7c931df35d7b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt @@ -352,8 +352,8 @@ abstract class MagnetizedObject<T : Any>( val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target -> val distanceFromTargetCenter = hypot( - ev.rawX - target.centerOnScreen.x, - ev.rawY - target.centerOnScreen.y) + ev.rawX - target.centerOnDisplayX(), + ev.rawY - target.centerOnDisplayY()) distanceFromTargetCenter < target.magneticFieldRadiusPx } @@ -406,7 +406,6 @@ abstract class MagnetizedObject<T : Any>( // First, check for relevant gestures concluding with an ACTION_UP. if (ev.action == MotionEvent.ACTION_UP) { - velocityTracker.computeCurrentVelocity(1000 /* units */) val velX = velocityTracker.xVelocity val velY = velocityTracker.yVelocity @@ -542,7 +541,7 @@ abstract class MagnetizedObject<T : Any>( // Whether velocity is sufficient, depending on whether we're flinging into a target at the // top or the bottom of the screen. val velocitySufficient = - if (rawY < target.centerOnScreen.y) velY > flingToTargetMinVelocity + if (rawY < target.centerOnDisplayY()) velY > flingToTargetMinVelocity else velY < flingToTargetMinVelocity if (!velocitySufficient) { @@ -560,15 +559,15 @@ abstract class MagnetizedObject<T : Any>( val yIntercept = rawY - slope * rawX // ...calculate the x value when y = the target's y-coordinate. - targetCenterXIntercept = (target.centerOnScreen.y - yIntercept) / slope + targetCenterXIntercept = (target.centerOnDisplayY() - yIntercept) / slope } // The width of the area we're looking for a fling towards. val targetAreaWidth = target.targetView.width * flingToTargetWidthPercent // Velocity was sufficient, so return true if the intercept is within the target area. - return targetCenterXIntercept > target.centerOnScreen.x - targetAreaWidth / 2 && - targetCenterXIntercept < target.centerOnScreen.x + targetAreaWidth / 2 + return targetCenterXIntercept > target.centerOnDisplayX() - targetAreaWidth / 2 && + targetCenterXIntercept < target.centerOnDisplayX() + targetAreaWidth / 2 } /** Cancel animations on this object's x/y properties. */ @@ -601,6 +600,22 @@ abstract class MagnetizedObject<T : Any>( ) { val centerOnScreen = PointF() + /** + * Set screen vertical offset amount. + * + * Screen surface may be vertically shifted in some cases, for example when one-handed mode + * is enabled. [MagneticTarget] and [MagnetizedObject] set their location in screen + * coordinates (see [MagneticTarget.centerOnScreen] and + * [MagnetizedObject.getLocationOnScreen] respectively). + * + * When a [MagnetizedObject] is dragged, the touch location is determined by + * [MotionEvent.getRawX] and [MotionEvent.getRawY]. These work in display coordinates. When + * screen is shifted due to one-handed mode, display coordinates and screen coordinates do + * not match. To determine if a [MagnetizedObject] is dragged into a [MagneticTarget], view + * location on screen is translated to display coordinates using this offset value. + */ + var screenVerticalOffset: Int = 0 + private val tempLoc = IntArray(2) fun updateLocationOnScreen() { @@ -614,6 +629,23 @@ abstract class MagnetizedObject<T : Any>( tempLoc[1] + targetView.height / 2f - targetView.translationY) } } + + /** + * Get target center coordinate on x-axis on display. [centerOnScreen] has to be up to date + * by calling [updateLocationOnScreen] first. + */ + fun centerOnDisplayX(): Float { + return centerOnScreen.x + } + + /** + * Get target center coordinate on y-axis on display. [centerOnScreen] has to be up to date + * by calling [updateLocationOnScreen] first. Use [screenVerticalOffset] to update the + * screen offset compared to the display. + */ + fun centerOnDisplayY(): Float { + return centerOnScreen.y + screenVerticalOffset + } } companion object { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt index 9f1ee6c92700..a9f054ec2b2f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt @@ -30,6 +30,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat import org.mockito.Mockito import org.mockito.Mockito.`when` @@ -97,7 +98,7 @@ class MagnetizedObjectTest : ShellTestCase() { // The mock target view will pretend that it's 200x200, and at (400, 800). This means it's // occupying the bounds (400, 800, 600, 1000) and it has a center of (500, 900). - `when`(targetView.width).thenReturn(targetSize) // width = 200 + `when`(targetView.width).thenReturn(targetSize) // width = 200 `when`(targetView.height).thenReturn(targetSize) // height = 200 doAnswer { invocation -> (invocation.arguments[0] as IntArray).also { location -> @@ -275,11 +276,11 @@ class MagnetizedObjectTest : ShellTestCase() { // Forcefully fling the object towards the target (but never touch the magnetic field). dispatchMotionEvents( getMotionEvent( - x = targetCenterX, + x = 0, y = 0, action = MotionEvent.ACTION_DOWN), getMotionEvent( - x = targetCenterX, + x = targetCenterX / 2, y = targetCenterY / 2), getMotionEvent( x = targetCenterX, @@ -405,15 +406,78 @@ class MagnetizedObjectTest : ShellTestCase() { verify(magnetListener).onStuckToTarget(magneticTarget) } + @Test + fun testMagneticTargetHasScreenOffset_moveIntoAndReleaseInTarget() { + magneticTarget.screenVerticalOffset = 500 + + dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY)) + // Moved into the target location, but it should be shifted due to screen offset. + // Should not get stuck. + verify(magnetListener, never()).onStuckToTarget(magneticTarget) + + dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY + 500)) + verify(magnetListener).onStuckToTarget(magneticTarget) + + dispatchMotionEvents( + getMotionEvent( + x = targetCenterX, + y = targetCenterY + 500, + action = MotionEvent.ACTION_UP + ) + ) + + verify(magnetListener).onReleasedInTarget(magneticTarget) + verifyNoMoreInteractions(magnetListener) + } + + @Test + fun testMagneticTargetHasScreenOffset_screenOffsetUpdates() { + magneticTarget.screenVerticalOffset = 500 + val adjustedTargetCenter = targetCenterY + 500 + + dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = adjustedTargetCenter)) + dispatchMotionEvents(getMotionEvent(x = 0, y = 0)) + verify(magnetListener).onStuckToTarget(magneticTarget) + verify(magnetListener) + .onUnstuckFromTarget(eq(magneticTarget), anyFloat(), anyFloat(), anyBoolean()) + + // Offset if removed, we should now get stuck at the target location + magneticTarget.screenVerticalOffset = 0 + dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY)) + verify(magnetListener, times(2)).onStuckToTarget(magneticTarget) + } + + @Test + fun testMagneticTargetHasScreenOffset_flingTowardsTarget() { + timeStep = 10 + + magneticTarget.screenVerticalOffset = 500 + val adjustedTargetCenter = targetCenterY + 500 + + // Forcefully fling the object towards the target (but never touch the magnetic field). + dispatchMotionEvents( + getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN), + getMotionEvent(x = targetCenterX / 2, y = adjustedTargetCenter / 2), + getMotionEvent( + x = targetCenterX, + y = adjustedTargetCenter - magneticFieldRadius * 2, + action = MotionEvent.ACTION_UP + ) + ) + + // Nevertheless it should have ended up stuck to the target. + verify(magnetListener, times(1)).onStuckToTarget(magneticTarget) + } + private fun getSecondMagneticTarget(): MagnetizedObject.MagneticTarget { // The first target view is at bounds (400, 800, 600, 1000) and it has a center of // (500, 900). We'll add a second one at bounds (0, 800, 200, 1000) with center (100, 900). val secondTargetView = mock(View::class.java) - var secondTargetCenterX = 100 - var secondTargetCenterY = 900 + val secondTargetCenterX = 100 + val secondTargetCenterY = 900 `when`(secondTargetView.context).thenReturn(context) - `when`(secondTargetView.width).thenReturn(targetSize) // width = 200 + `when`(secondTargetView.width).thenReturn(targetSize) // width = 200 `when`(secondTargetView.height).thenReturn(targetSize) // height = 200 doAnswer { invocation -> (invocation.arguments[0] as Runnable).run() |