diff options
| author | 2025-01-08 01:07:55 +0000 | |
|---|---|---|
| committer | 2025-01-10 13:13:57 -0800 | |
| commit | 0912c28ad74946f8545732a6989e88ba6bade096 (patch) | |
| tree | 4df6a365507510712daf5dff6901e975db5e750b | |
| parent | e64e000f2a17c399fcaba2c8d8e2f802465920d8 (diff) | |
Handling DisplayCutouts more granularly to allow the FAB to occupy more space
Flag: com.android.systemui.floating_menu_display_cutout_support
Bug: 384399408
Test: atest com.android.systemup.floatingmenu
Change-Id: I37095dffc48cb653a44d8be1986667f7c282844e
8 files changed, 148 insertions, 14 deletions
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig index b5eba08c8f87..585d021baa98 100644 --- a/packages/SystemUI/aconfig/accessibility.aconfig +++ b/packages/SystemUI/aconfig/accessibility.aconfig @@ -38,6 +38,16 @@ flag { } flag { + name: "floating_menu_display_cutout_support" + namespace: "accessibility" + description: "Makes FAB properly react to and avoid DisplayCutouts." + bug: "384399408" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "floating_menu_drag_to_hide" namespace: "accessibility" description: "Allows users to hide the FAB then use notification to dismiss or bring it back." diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java index 56a97bb34172..fff6def52803 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import android.graphics.PointF; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; @@ -210,7 +211,7 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase { mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent); mTouchHandler.onInterceptTouchEvent(mStubListView, stubUpEvent); - verify(mMenuAnimationController).flingMenuThenSpringToEdge(anyFloat(), anyFloat(), + verify(mMenuAnimationController).flingMenuThenSpringToEdge(any(PointF.class), anyFloat(), anyFloat()); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java index 030d147e21c3..edbede8fa865 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java @@ -24,6 +24,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.Looper; import android.util.Log; +import android.view.DisplayCutout; import android.view.View; import android.view.animation.Animation; import android.view.animation.OvershootInterpolator; @@ -197,7 +198,7 @@ class MenuAnimationController { constrainPositionAndUpdate(position, /* writeToPosition = */ true); } - void flingMenuThenSpringToEdge(float x, float velocityX, float velocityY) { + void flingMenuThenSpringToEdge(PointF position, float velocityX, float velocityY) { final boolean shouldMenuFlingLeft = isOnLeftSide() ? velocityX < ESCAPE_VELOCITY : velocityX < -ESCAPE_VELOCITY; @@ -205,9 +206,17 @@ class MenuAnimationController { final Rect draggableBounds = mMenuView.getMenuDraggableBounds(); final float finalPositionX = shouldMenuFlingLeft ? draggableBounds.left : draggableBounds.right; - + final DisplayCutout displayCutout = mMenuViewAppearance.getDisplayCutout(); + final float finalPositionY = + (displayCutout == null) ? position.y + : mMenuViewAppearance.avoidVerticalDisplayCutout( + position.y, draggableBounds, + shouldMenuFlingLeft + ? displayCutout.getBoundingRectLeft() + : displayCutout.getBoundingRectRight() + ); final float minimumVelocityToReachEdge = - (finalPositionX - x) * (FLING_FRICTION_SCALAR * DEFAULT_FRICTION); + (finalPositionX - position.x) * (FLING_FRICTION_SCALAR * DEFAULT_FRICTION); final float startXVelocity = shouldMenuFlingLeft ? Math.min(minimumVelocityToReachEdge, velocityX) @@ -219,11 +228,19 @@ class MenuAnimationController { createSpringForce(), finalPositionX); - flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y, - velocityY, - FLING_FRICTION_SCALAR, - createSpringForce(), - /* finalPosition= */ null); + if (com.android.systemui.Flags.floatingMenuDisplayCutoutSupport()) { + flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y, + velocityY, + FLING_FRICTION_SCALAR, + createSpringForce(), + (finalPositionY != position.y) ? finalPositionY : null); + } else { + flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y, + velocityY, + FLING_FRICTION_SCALAR, + createSpringForce(), + /* finalPosition= */ null); + } } private void flingThenSpringMenuWith(DynamicAnimation.ViewProperty property, float velocity, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java index 9511e3769a8d..aca020d235be 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java @@ -105,7 +105,8 @@ class MenuListViewTouchHandler implements RecyclerView.OnItemTouchListener { if (mDragToInteractAnimationController.maybeConsumeUpMotionEvent(motionEvent) == empty) { mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT_SECONDS); - mMenuAnimationController.flingMenuThenSpringToEdge(endX, + mMenuAnimationController.flingMenuThenSpringToEdge( + new PointF(endX, mMenuTranslationDown.y + dy), mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); mMenuAnimationController.fadeOutIfEnabled(); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java index a700cbef2e16..bd3dfe049587 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java @@ -28,12 +28,14 @@ import android.graphics.Insets; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.view.DisplayCutout; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; import androidx.annotation.DimenRes; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.res.R; import java.lang.annotation.Retention; @@ -291,7 +293,7 @@ class MenuViewAppearance { final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics(); final WindowInsets windowInsets = windowMetrics.getWindowInsets(); final Insets insets = windowInsets.getInsetsIgnoringVisibility( - WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()); + WindowInsets.Type.systemBars()); final Rect bounds = new Rect(windowMetrics.getBounds()); bounds.left += insets.left; @@ -302,6 +304,37 @@ class MenuViewAppearance { return bounds; } + DisplayCutout getDisplayCutout() { + return mWindowManager.getCurrentWindowMetrics().getWindowInsets().getDisplayCutout(); + } + + float avoidVerticalDisplayCutout(float y, Rect bounds, Rect cutout) { + int menuHeight = calculateActualMenuHeight(); + return avoidVerticalDisplayCutout(y, menuHeight, bounds, cutout); + } + + @VisibleForTesting + public static float avoidVerticalDisplayCutout( + float y, float menuHeight, Rect bounds, Rect cutout) { + if (cutout.top > y + menuHeight || cutout.bottom < y) { + return y; + } + + boolean topAvailable = cutout.top - bounds.top >= menuHeight; + boolean bottomAvailable = bounds.bottom - cutout.bottom >= menuHeight; + boolean topOrBottom; + if (!topAvailable && !bottomAvailable) { + return y; + } else if (topAvailable && !bottomAvailable) { + topOrBottom = true; + } else if (!topAvailable && bottomAvailable) { + topOrBottom = false; + } else { + topOrBottom = y + menuHeight * 0.5f < cutout.centerY(); + } + return (topOrBottom) ? cutout.top - menuHeight : cutout.bottom; + } + boolean isMenuOnLeftSide() { return mPercentagePosition.getPercentageX() < 0.5f; } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java index 7a674e2fa6f1..2ee9de4f49d8 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -516,7 +516,7 @@ class MenuViewLayer extends FrameLayout implements return; } mMenuAnimationController.flingMenuThenSpringToEdge( - mMenuView.getMenuPosition().x, 100f, 0f); + mMenuView.getMenuPosition(), 100f, 0f); Intent intent = getIntentForEditScreen(); PackageManager packageManager = getContext().getPackageManager(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java index 9f6ad56335d7..c14cb87064ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java @@ -194,7 +194,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase { final Runnable onSpringAnimationsEndCallback = mock(Runnable.class); mMenuAnimationController.setSpringAnimationsEndAction(onSpringAnimationsEndCallback); - mMenuAnimationController.flingMenuThenSpringToEdge(/* x= */ 0, /* velocityX= */ + mMenuAnimationController.flingMenuThenSpringToEdge(new PointF(), /* velocityX= */ 100, /* velocityY= */ 100); mMenuAnimationController.mPositionAnimations.values() .forEach(animation -> verify((FlingAnimation) animation).addEndListener( @@ -212,7 +212,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase { final Runnable onSpringAnimationsEndCallback = mock(Runnable.class); mMenuAnimationController.setSpringAnimationsEndAction(onSpringAnimationsEndCallback); - mMenuAnimationController.flingMenuThenSpringToEdge(/* x= */ 0, /* velocityX= */ + mMenuAnimationController.flingMenuThenSpringToEdge(new PointF(), /* velocityX= */ 200, /* velocityY= */ 200); mMenuAnimationController.mPositionAnimations.values() .forEach(animation -> verify((FlingAnimation) animation).addEndListener( diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java new file mode 100644 index 000000000000..146488b523ad --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2025 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.accessibility.floatingmenu; + +import static com.google.common.truth.Truth.assertThat; + +import android.graphics.Rect; +import android.testing.TestableLooper; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Tests for {@link MenuViewAppearanceTest}. */ +@RunWith(AndroidJUnit4.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class MenuViewAppearanceTest extends SysuiTestCase { + static final Rect DRAGGABLE_BOUNDS = new Rect(0, 0, 10, 10); + static final int MENU_HEIGHT = 1; + + @Test + public void avoidVerticalDisplayCutout_roomAbove_placesAbove() { + final int y = 2; + final Rect cutout = new Rect(0, 3, 0, 10); + + final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout( + y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout); + + assertThat(end_y + MENU_HEIGHT).isAtMost(cutout.top); + } + + @Test + public void avoidVerticalDisplayCutout_roomBelow_placesBelow() { + final int y = 2; + final Rect cutout = new Rect(0, 0, 0, 5); + + final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout( + y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout); + + assertThat(end_y).isAtLeast(cutout.bottom); + } + + @Test + public void avoidVerticalDisplayCutout_noRoom_noChange() { + final int y = 2; + final Rect cutout = new Rect(0, 0, 0, 10); + + final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout( + y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout); + + assertThat(end_y).isEqualTo(end_y); + } +} |