diff options
6 files changed, 138 insertions, 33 deletions
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt new file mode 100644 index 000000000000..9bee11a92430 --- /dev/null +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt @@ -0,0 +1,29 @@ +/* + * 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.wm.shell.shared.bubbles + +import android.graphics.Rect + +/** + * Provide bounds for Bubbles drop targets that are shown when dragging over drag zones + */ +interface BubbleDropTargetBoundsProvider { + /** + * Get bubble bar expanded view visual drop target bounds on screen + */ + fun getBubbleBarExpandedViewDropTargetBounds(onLeft: Boolean): Rect +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 436e479796ac..2c81945ffdbe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -116,6 +116,7 @@ import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import com.android.wm.shell.shared.bubbles.BubbleBarUpdate; +import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider; import com.android.wm.shell.shared.bubbles.DeviceConfig; import com.android.wm.shell.sysui.ConfigurationChangeListener; import com.android.wm.shell.sysui.ShellCommandHandler; @@ -925,6 +926,11 @@ public class BubbleController implements ConfigurationChangeListener, return mBubblePositioner; } + /** Provides bounds for drag zone drop targets */ + public BubbleDropTargetBoundsProvider getBubbleDropTargetBoundsProvider() { + return mBubblePositioner; + } + BubbleIconFactory getIconFactory() { return mBubbleIconFactory; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 8cf3f7afd46a..5273a7cf2432 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -27,19 +27,21 @@ import android.graphics.RectF; import android.view.Surface; import android.view.WindowManager; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconNormalizer; import com.android.wm.shell.R; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider; import com.android.wm.shell.shared.bubbles.DeviceConfig; /** * Keeps track of display size, configuration, and specific bubble sizes. One place for all * placement and positioning calculations to refer to. */ -public class BubblePositioner { +public class BubblePositioner implements BubbleDropTargetBoundsProvider { /** The screen edge the bubble stack is pinned to */ public enum StackPinnedEdge { @@ -100,6 +102,7 @@ public class BubblePositioner { private int mManageButtonHeight; private int mOverflowHeight; private int mMinimumFlyoutWidthLargeScreen; + private int mBubbleBarExpandedViewDropTargetPadding; private PointF mRestingStackPosition; @@ -164,6 +167,8 @@ public class BubblePositioner { res.getDimensionPixelSize(R.dimen.bubble_bar_expanded_view_width), mPositionRect.width() - 2 * mExpandedViewPadding ); + mBubbleBarExpandedViewDropTargetPadding = res.getDimensionPixelSize( + R.dimen.bubble_bar_expanded_view_drop_target_padding); if (mShowingInBubbleBar) { mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth; @@ -965,4 +970,14 @@ public class BubblePositioner { int top = getExpandedViewBottomForBubbleBar() - height; out.offsetTo(left, top); } + + @NonNull + @Override + public Rect getBubbleBarExpandedViewDropTargetBounds(boolean onLeft) { + Rect bounds = new Rect(); + getBubbleBarExpandedViewBounds(onLeft, false, bounds); + bounds.inset(mBubbleBarExpandedViewDropTargetPadding, + mBubbleBarExpandedViewDropTargetPadding); + return bounds; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index 27ccb8bc71fe..27aed17762ff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -60,6 +60,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper; +import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; /** @@ -119,6 +120,7 @@ public class DesktopModeVisualIndicator { private final RootTaskDisplayAreaOrganizer mRootTdaOrganizer; private final ActivityManager.RunningTaskInfo mTaskInfo; private final SurfaceControl mTaskSurface; + private final @Nullable BubbleDropTargetBoundsProvider mBubbleBoundsProvider; private SurfaceControl mLeash; private final SyncTransactionQueue mSyncQueue; @@ -133,13 +135,15 @@ public class DesktopModeVisualIndicator { ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController, Context context, SurfaceControl taskSurface, RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, - DragStartState dragStartState) { + DragStartState dragStartState, + @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider) { mSyncQueue = syncQueue; mTaskInfo = taskInfo; mDisplayController = displayController; mContext = context; mTaskSurface = taskSurface; mRootTdaOrganizer = taskDisplayAreaOrganizer; + mBubbleBoundsProvider = bubbleBoundsProvider; mCurrentType = NO_INDICATOR; mDragStartState = dragStartState; } @@ -329,6 +333,11 @@ public class DesktopModeVisualIndicator { }); } + @VisibleForTesting + Rect getIndicatorBounds() { + return mView.getBackground().getBounds(); + } + /** * Fade indicator in as provided type. Animator fades it in while expanding the bounds outwards. */ @@ -336,7 +345,8 @@ public class DesktopModeVisualIndicator { mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background); final VisualIndicatorAnimator animator = VisualIndicatorAnimator .fadeBoundsIn(mView, type, - mDisplayController.getDisplayLayout(mTaskInfo.displayId)); + mDisplayController.getDisplayLayout(mTaskInfo.displayId), + mBubbleBoundsProvider); animator.start(); mCurrentType = type; } @@ -355,7 +365,8 @@ public class DesktopModeVisualIndicator { } final VisualIndicatorAnimator animator = VisualIndicatorAnimator .fadeBoundsOut(mView, mCurrentType, - mDisplayController.getDisplayLayout(mTaskInfo.displayId)); + mDisplayController.getDisplayLayout(mTaskInfo.displayId), + mBubbleBoundsProvider); animator.start(); if (finishCallback != null) { animator.addListener(new AnimatorListenerAdapter() { @@ -383,7 +394,7 @@ public class DesktopModeVisualIndicator { } else { final VisualIndicatorAnimator animator = VisualIndicatorAnimator.animateIndicatorType( mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType, - newType); + newType, mBubbleBoundsProvider); mCurrentType = newType; animator.start(); } @@ -438,8 +449,9 @@ public class DesktopModeVisualIndicator { } private static VisualIndicatorAnimator fadeBoundsIn( - @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) { - final Rect endBounds = getIndicatorBounds(displayLayout, type); + @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout, + @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider) { + final Rect endBounds = getIndicatorBounds(displayLayout, type, bubbleBoundsProvider); final Rect startBounds = getMinBounds(endBounds); view.getBackground().setBounds(startBounds); @@ -451,8 +463,9 @@ public class DesktopModeVisualIndicator { } private static VisualIndicatorAnimator fadeBoundsOut( - @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) { - final Rect startBounds = getIndicatorBounds(displayLayout, type); + @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout, + @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider) { + final Rect startBounds = getIndicatorBounds(displayLayout, type, bubbleBoundsProvider); final Rect endBounds = getMinBounds(startBounds); view.getBackground().setBounds(startBounds); @@ -467,16 +480,19 @@ public class DesktopModeVisualIndicator { * Create animator for visual indicator changing type (i.e., fullscreen to freeform, * freeform to split, etc.) * - * @param view the view for this indicator - * @param displayLayout information about the display the transitioning task is currently on - * @param origType the original indicator type - * @param newType the new indicator type + * @param view the view for this indicator + * @param displayLayout information about the display the transitioning task is + * currently on + * @param origType the original indicator type + * @param newType the new indicator type + * @param bubbleBoundsProvider provides bounds for bubbles indicators */ private static VisualIndicatorAnimator animateIndicatorType(@NonNull View view, - @NonNull DisplayLayout displayLayout, IndicatorType origType, - IndicatorType newType) { - final Rect startBounds = getIndicatorBounds(displayLayout, origType); - final Rect endBounds = getIndicatorBounds(displayLayout, newType); + @NonNull DisplayLayout displayLayout, IndicatorType origType, IndicatorType newType, + @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider) { + final Rect startBounds = getIndicatorBounds(displayLayout, origType, + bubbleBoundsProvider); + final Rect endBounds = getIndicatorBounds(displayLayout, newType, bubbleBoundsProvider); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( view, startBounds, endBounds); animator.setInterpolator(new DecelerateInterpolator()); @@ -485,7 +501,8 @@ public class DesktopModeVisualIndicator { } /** Calculates the bounds the indicator should have when fully faded in. */ - private static Rect getIndicatorBounds(DisplayLayout layout, IndicatorType type) { + private static Rect getIndicatorBounds(DisplayLayout layout, IndicatorType type, + @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider) { final Rect desktopStableBounds = new Rect(); layout.getStableBounds(desktopStableBounds); final int padding = desktopStableBounds.top; @@ -514,20 +531,17 @@ public class DesktopModeVisualIndicator { desktopStableBounds.width() - padding, desktopStableBounds.height()); case TO_BUBBLE_LEFT_INDICATOR: - // TODO(b/388851898): define based on bubble size on the device - return new Rect( - padding, - desktopStableBounds.height() / 2 - padding, - desktopStableBounds.width() / 2 - padding, - desktopStableBounds.height()); + if (bubbleBoundsProvider == null) { + return new Rect(); + } + return bubbleBoundsProvider.getBubbleBarExpandedViewDropTargetBounds( + /* onLeft= */ true); case TO_BUBBLE_RIGHT_INDICATOR: - // TODO(b/388851898): define based on bubble size on the device - return new Rect( - desktopStableBounds.width() / 2 + padding, - desktopStableBounds.height() / 2 - padding, - desktopStableBounds.width() - padding, - desktopStableBounds.height() - ); + if (bubbleBoundsProvider == null) { + return new Rect(); + } + return bubbleBoundsProvider.getBubbleBarExpandedViewDropTargetBounds( + /* onLeft= */ false); default: throw new IllegalArgumentException("Invalid indicator type provided."); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index cf724f5031a6..0d32acd6b068 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -150,6 +150,7 @@ import java.util.Optional import java.util.concurrent.Executor import java.util.concurrent.TimeUnit import java.util.function.Consumer +import kotlin.jvm.optionals.getOrNull /** Handles moving tasks in and out of desktop */ class DesktopTasksController( @@ -2706,6 +2707,7 @@ class DesktopTasksController( taskSurface, rootTaskDisplayAreaOrganizer, dragStartState, + bubbleController.getOrNull()?.bubbleDropTargetBoundsProvider, ) if (visualIndicator == null) visualIndicator = indicator return indicator.updateIndicatorType(PointF(inputX, taskTop)) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt index c5db2c592aa0..a6575535faee 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt @@ -16,11 +16,11 @@ package com.android.wm.shell.desktopmode +import android.animation.AnimatorTestRule import android.app.ActivityManager.RunningTaskInfo import android.graphics.PointF import android.graphics.Rect import android.platform.test.annotations.EnableFlags -import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.SurfaceControl @@ -34,6 +34,7 @@ import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -56,7 +57,7 @@ import org.mockito.kotlin.whenever @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) class DesktopModeVisualIndicatorTest : ShellTestCase() { - @JvmField @Rule val setFlagsRule = SetFlagsRule() + @JvmField @Rule val animatorTestRule = AnimatorTestRule(this) @JvmField @Rule @@ -69,6 +70,7 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { @Mock private lateinit var taskSurface: SurfaceControl @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @Mock private lateinit var displayLayout: DisplayLayout + @Mock private lateinit var bubbleBoundsProvider: BubbleDropTargetBoundsProvider private lateinit var visualIndicator: DesktopModeVisualIndicator @@ -80,6 +82,8 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS) whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout) whenever(displayController.getDisplay(anyInt())).thenReturn(mContext.display) + whenever(bubbleBoundsProvider.getBubbleBarExpandedViewDropTargetBounds(any())) + .thenReturn(Rect()) taskInfo = DesktopTestHelpers.createFullscreenTask() } @@ -292,6 +296,40 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { assertThat(result).isEqualTo(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR) } + @Test + @EnableFlags( + com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, + com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE, + ) + fun testBubbleLeftVisualIndicatorSize() { + val dropTargetBounds = Rect(100, 100, 500, 1500) + whenever(bubbleBoundsProvider.getBubbleBarExpandedViewDropTargetBounds(/* onLeft= */ true)) + .thenReturn(dropTargetBounds) + createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN) + visualIndicator.updateIndicatorType(PointF(100f, 1500f)) + + animatorTestRule.advanceTimeBy(200) + + assertThat(visualIndicator.indicatorBounds).isEqualTo(dropTargetBounds) + } + + @Test + @EnableFlags( + com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, + com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE, + ) + fun testBubbleRightVisualIndicatorSize() { + val dropTargetBounds = Rect(1900, 100, 2300, 1500) + whenever(bubbleBoundsProvider.getBubbleBarExpandedViewDropTargetBounds(/* onLeft= */ false)) + .thenReturn(dropTargetBounds) + createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN) + visualIndicator.updateIndicatorType(PointF(2300f, 1500f)) + + animatorTestRule.advanceTimeBy(200) + + assertThat(visualIndicator.indicatorBounds).isEqualTo(dropTargetBounds) + } + private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) { visualIndicator = DesktopModeVisualIndicator( @@ -302,6 +340,7 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { taskSurface, taskDisplayAreaOrganizer, dragStartState, + bubbleBoundsProvider, ) } |