Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7a1e6a3 authored by Vania Desmonda's avatar Vania Desmonda Committed by Android (Google) Code Review
Browse files

Merge "Set PiP bounds using local px to prevent wrapping on the origin display." into main

parents 003402b2 802c2a70
Loading
Loading
Loading
Loading
+2 −26
Original line number Diff line number Diff line
@@ -15,11 +15,8 @@
 */
package com.android.wm.shell.pip2.phone;

import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE;

import android.annotation.Nullable;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
@@ -30,7 +27,6 @@ import android.view.SurfaceControl.Transaction;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
@@ -124,29 +120,9 @@ public class PipDisplayTransferHandler implements
     * position.
     *
     * @param originDisplayId           the display ID where the drag originated from
     * @param currentDisplayId          the current display ID the pointer is on
     * @param originPointerCoordinates  the position of the pointer when it started dragging
     * @param currentPointerCoordinates the position of the pointer it's currently on
     * @param originBounds              the PiP bounds when dragging starts
     * @param globalDpPipBounds         the PiP bounds in display topology-aware global DP
     */
    public void showDragMirrorOnConnectedDisplays(int originDisplayId, int currentDisplayId,
            PointF originPointerCoordinates, PointF currentPointerCoordinates, Rect originBounds) {
        DisplayLayout originDisplayLayout = mDisplayController.getDisplayLayout(originDisplayId);
        DisplayLayout currentDisplayLayout = mDisplayController.getDisplayLayout(currentDisplayId);

        if (originDisplayLayout == null || currentDisplayLayout == null) {
            ProtoLog.w(WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Failed to show drag mirror on connected displays because displayLayout "
                            + "is null", TAG);
            return;
        }

        RectF globalDpPipBounds =
                MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
                originDisplayLayout, originPointerCoordinates, originBounds, currentDisplayLayout,
                currentPointerCoordinates.x, currentPointerCoordinates.y
        );

    public void showDragMirrorOnConnectedDisplays(int originDisplayId, RectF globalDpPipBounds) {
        final Transaction transaction = mSurfaceControlTransactionFactory.getTransaction();
        // Iterate through each connected display ID to ensure partial PiP bounds are shown on
        // all corresponding displays while dragging
+44 −19
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
@@ -53,7 +54,9 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -102,6 +105,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
    private final PipDisplayTransferHandler mPipDisplayTransferHandler;
    private final PhonePipMenuController mMenuController;
    private final AccessibilityManager mAccessibilityManager;
    private final DisplayController mDisplayController;

    /**
     * Whether PIP stash is enabled or not. When enabled, if the user flings toward the edge of the
@@ -205,6 +209,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
        mPipBoundsAlgorithm = pipBoundsAlgorithm;
        mPipBoundsState = pipBoundsState;
        mPipDesktopState = pipDesktopState;
        mDisplayController = displayController;

        mPipTransitionState = pipTransitionState;
        mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
@@ -220,7 +225,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha
        mPipDisplayTransferHandler = pipDisplayTransferHandler;
        mPipScheduler.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
        mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
                mMotionHelper, mPipDisplayLayoutState, displayController, mainExecutor);
                mMotionHelper, mPipDisplayLayoutState, mDisplayController, mainExecutor);
        mTouchState = new PipTouchState(ViewConfiguration.get(context),
                () -> {
                    mMenuController.showMenuWithPossibleDelay(MENU_STATE_FULL,
@@ -868,7 +873,34 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha

            if (touchState.isDragging()) {
                mPipBoundsState.setHasUserMovedPip(true);
                final PointF curPos = touchState.getLastTouchPosition();

                if (mPipDesktopState.isDraggingPipAcrossDisplaysEnabled()) {
                    DisplayLayout currentDisplayLayout = mDisplayController.getDisplayLayout(
                            touchState.getLastTouchDisplayId());
                    DisplayLayout displayLayoutOnDown = mDisplayController.getDisplayLayout(
                            mDisplayIdOnDown);

                    if (displayLayoutOnDown == null || currentDisplayLayout == null) {
                        ProtoLog.w(WM_SHELL_PICTURE_IN_PICTURE,
                                "%s: Failed to show drag mirror on connected displays because "
                                        + "displayLayout is null", TAG);
                        return false;
                    }

                    RectF globalDpPipBounds =
                            MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
                                    displayLayoutOnDown, mPointerPositionOnDown, mStartBounds,
                                    currentDisplayLayout, curPos.x, curPos.y);

                    // Create mirrors on connected displays to simulate dragging PiP across displays
                    mPipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(mDisplayIdOnDown,
                            globalDpPipBounds);
                    // Set PiP bounds on the origin display in display topology-aware local px
                    mTmpBounds.set(
                            MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
                                    globalDpPipBounds, displayLayoutOnDown));
                } else {
                    // Move the pinned stack freely
                    final PointF lastDelta = touchState.getLastTouchDelta();
                    float lastX = mStartBounds.left + mDelta.x;
@@ -882,17 +914,10 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha

                    mTmpBounds.set(getPossiblyMotionBounds());
                    mTmpBounds.offsetTo((int) left, (int) top);
                mMotionHelper.movePip(mTmpBounds, true /* isDragging */);

                final PointF curPos = touchState.getLastTouchPosition();

                if (mPipDesktopState.isDraggingPipAcrossDisplaysEnabled()) {
                    // Create mirrors on connected displays to simulate dragging PiP across displays
                    mPipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(mDisplayIdOnDown,
                            touchState.getLastTouchDisplayId(), mPointerPositionOnDown, curPos,
                            mStartBounds);
                }

                mMotionHelper.movePip(mTmpBounds, true /* isDragging */);

                if (mMovementWithinDismiss) {
                    // Track if movement remains near the bottom edge to identify swipe to dismiss
                    mMovementWithinDismiss = curPos.y >= mPipBoundsState.getMovementBounds().bottom;
+18 −12
Original line number Diff line number Diff line
@@ -43,7 +43,6 @@ import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import android.os.Bundle
import android.testing.TestableResources
import android.util.ArrayMap
@@ -60,6 +59,8 @@ import org.mockito.kotlin.eq
import org.mockito.kotlin.times
import com.google.common.truth.Truth.assertThat
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator
import com.android.wm.shell.common.MultiDisplayTestUtil.TestDisplay
import org.junit.Rule
import org.mockito.kotlin.never
@@ -92,6 +93,7 @@ class PipDisplayTransferHandlerTest : ShellTestCase() {
    private lateinit var pipDisplayTransferHandler: PipDisplayTransferHandler

    private val displayIds = intArrayOf(ORIGIN_DISPLAY_ID, TARGET_DISPLAY_ID, SECONDARY_DISPLAY_ID)
    private val displayLayouts = ArrayMap<Int, DisplayLayout>()
    private val mockBounds = Rect()

    @JvmField
@@ -137,6 +139,7 @@ class PipDisplayTransferHandlerTest : ShellTestCase() {
            whenever(mockDisplayController.getDisplay(id)).thenReturn(display)
            val displayLayout =
                TestDisplay.entries.find { it.id == id }?.getSpyDisplayLayout(resources)
            displayLayouts.put(id, displayLayout)
            whenever(mockDisplayController.getDisplayLayout(id)).thenReturn(displayLayout)
        }

@@ -189,10 +192,11 @@ class PipDisplayTransferHandlerTest : ShellTestCase() {

    @Test
    fun showDragMirrorOnConnectedDisplays_hasNotLeftOriginDisplay_shouldNotCreateMirrors() {
        val globalDpBounds = MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
            displayLayouts.get(ORIGIN_DISPLAY_ID)!!, START_DRAG_COORDINATES,
            PIP_BOUNDS, displayLayouts.get(ORIGIN_DISPLAY_ID)!!, 150f, 150f)
        pipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(
            ORIGIN_DISPLAY_ID, ORIGIN_DISPLAY_ID,
            START_DRAG_COORDINATES, PointF(150f, 150f),
            PIP_BOUNDS
            ORIGIN_DISPLAY_ID, globalDpBounds
        )

        verify(mockRootTaskDisplayAreaOrganizer, never()).reparentToDisplayArea(
@@ -214,10 +218,12 @@ class PipDisplayTransferHandlerTest : ShellTestCase() {

    @Test
    fun showDragMirrorOnConnectedDisplays_movedToAnotherDisplay_createsOneMirror() {
        val globalDpBounds = MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
            displayLayouts.get(ORIGIN_DISPLAY_ID)!!, START_DRAG_COORDINATES,
            PIP_BOUNDS, displayLayouts.get(TARGET_DISPLAY_ID)!!,
            TestDisplay.DISPLAY_1.bounds.centerX(), TestDisplay.DISPLAY_1.bounds.centerY())
        pipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(
            ORIGIN_DISPLAY_ID, TARGET_DISPLAY_ID,
            START_DRAG_COORDINATES, TestDisplay.DISPLAY_1.bounds.center(),
            PIP_BOUNDS
            ORIGIN_DISPLAY_ID, globalDpBounds
        )

        verify(mockRootTaskDisplayAreaOrganizer).reparentToDisplayArea(
@@ -240,10 +246,12 @@ class PipDisplayTransferHandlerTest : ShellTestCase() {

    @Test
    fun showDragMirrorOnConnectedDisplays_inBetweenThreeDisplays_createsTwoMirrors() {
        val globalDpBounds = MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
            displayLayouts.get(ORIGIN_DISPLAY_ID)!!, START_DRAG_COORDINATES,
            PIP_BOUNDS, displayLayouts.get(TARGET_DISPLAY_ID)!!,
            1000f, -100f)
        pipDisplayTransferHandler.showDragMirrorOnConnectedDisplays(
            ORIGIN_DISPLAY_ID, TARGET_DISPLAY_ID,
            START_DRAG_COORDINATES, PointF(1000f, -100f),
            PIP_BOUNDS
            ORIGIN_DISPLAY_ID, globalDpBounds
        )

        verify(mockRootTaskDisplayAreaOrganizer).reparentToDisplayArea(
@@ -290,8 +298,6 @@ class PipDisplayTransferHandlerTest : ShellTestCase() {
        assertThat(pipDisplayTransferHandler.mOnDragMirrorPerDisplayId.isEmpty()).isTrue()
    }

    fun RectF.center(): PointF = PointF(this.centerX(), this.centerY())

    companion object {
        const val ORIGIN_DISPLAY_ID = 0
        const val TARGET_DISPLAY_ID = 1
+43 −9
Original line number Diff line number Diff line
@@ -17,13 +17,18 @@ package com.android.wm.shell.pip2.phone

import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.modules.utils.testing.ExtendedMockitoRule
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.FloatingContentCoordinator
import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.pip.PipBoundsAlgorithm
import com.android.wm.shell.common.pip.PipBoundsState
@@ -37,6 +42,7 @@ import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import java.util.Optional
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
@@ -76,13 +82,20 @@ class PipTouchHandlerTest : ShellTestCase() {
    private val pipTouchState = mock<PipTouchState>()
    private val mockMotionBoundsState = mock<PipBoundsState.MotionBoundsState>()
    private val mockLeash = mock<SurfaceControl>()
    private val mockBounds = Rect()
    private val mockDisplayLayout = mock<DisplayLayout>()
    private val mockTouchPosition = PointF()
    private val mockPipSurfaceTransactionHelper = mock<PipSurfaceTransactionHelper>()

    private lateinit var pipTouchHandler: PipTouchHandler
    private lateinit var pipTouchGesture: PipTouchGesture

    @JvmField
    @Rule
    val extendedMockitoRule =
        ExtendedMockitoRule.Builder(this)
            .mockStatic(MultiDisplayDragMoveBoundsCalculator::class.java)
            .build()!!

    @Before
    fun setUp() {
        pipTouchHandler = PipTouchHandler(
@@ -102,9 +115,19 @@ class PipTouchHandlerTest : ShellTestCase() {
        whenever(pipTouchState.lastTouchDisplayId).thenReturn(ORIGIN_DISPLAY_ID)
        whenever(pipTouchState.lastTouchDelta).thenReturn(mockTouchPosition)
        whenever(pipTransitionState.pinnedTaskLeash).thenReturn(mockLeash)
        whenever(mockPipBoundsState.movementBounds).thenReturn(mockBounds)
        whenever(mockPipBoundsState.movementBounds).thenReturn(PIP_BOUNDS)
        whenever(mockPipBoundsState.motionBoundsState).thenReturn(mockMotionBoundsState)
        whenever(pipTouchHandler.possiblyMotionBounds).thenReturn(mockBounds)
        whenever(pipTouchHandler.possiblyMotionBounds).thenReturn(PIP_BOUNDS)
        whenever(mockDisplayController.getDisplayLayout(anyInt()))
            .thenReturn(mockDisplayLayout)
        whenever(
            MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(any(), any())
        ).thenReturn(PIP_BOUNDS)
        whenever(
            MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
                any(), any(), any(), any(), any(), any()
            )
        ).thenReturn(GLOBAL_BOUNDS)
    }

    @Test
@@ -112,17 +135,26 @@ class PipTouchHandlerTest : ShellTestCase() {
        whenever(mockPipDesktopState.isDraggingPipAcrossDisplaysEnabled()).thenReturn(true)
        whenever(pipTouchState.isUserInteracting).thenReturn(true)
        whenever(pipTouchState.isDragging).thenReturn(true)
        // Initialize variables that are set on down
        pipTouchGesture.onDown(pipTouchState)

        pipTouchGesture.onMove(pipTouchState)

        verify(mockPipDisplayTransferHandler).showDragMirrorOnConnectedDisplays(
            anyInt(),
            anyInt(),
            any(),
            any(),
            any()
        ExtendedMockito.verify {
            MultiDisplayDragMoveBoundsCalculator.calculateGlobalDpBoundsForDrag(
                any(), any(), any(), any(), any(), any()
            )
        }
        ExtendedMockito.verify {
            MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
                eq(GLOBAL_BOUNDS),
                eq(mockDisplayLayout)
            )
        }
        verify(mockPipDisplayTransferHandler).showDragMirrorOnConnectedDisplays(
            eq(ORIGIN_DISPLAY_ID), eq(GLOBAL_BOUNDS))
        verify(mockPipMotionHelper).movePip(eq(PIP_BOUNDS), eq(true))
    }

    @Test
    fun pipTouchGesture_crossDisplayDragFlagEnabled_onUpOnADifferentDisplay_schedulesMovePip() {
@@ -153,5 +185,7 @@ class PipTouchHandlerTest : ShellTestCase() {
    private companion object {
        const val ORIGIN_DISPLAY_ID = 0
        const val TARGET_DISPLAY_ID = 1
        val PIP_BOUNDS = Rect(0, 0, 700, 700)
        val GLOBAL_BOUNDS = RectF(0f, 0f, 400f, 400f)
    }
}
 No newline at end of file