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

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

Merge "[Pip-CD] (1/n) Add prototype to move PiP window to another display." into main

parents 83fa5c0c d7587b23
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ public enum DesktopExperienceFlags {
    ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS(Flags::enableDisplayFocusInShellTransitions, true),
    ENABLE_DISPLAY_RECONNECT_INTERACTION(Flags::enableDisplayReconnectInteraction, false),
    ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING(Flags::enableDisplayWindowingModeSwitching, true),
    ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS(Flags::enableDraggingPipAcrossDisplays, false),
    ENABLE_DRAG_TO_MAXIMIZE(Flags::enableDragToMaximize, true),
    ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX(Flags::enableDynamicRadiusComputationBugfix, false),
    ENABLE_FREEFORM_BOX_SHADOWS(Flags::enableFreeformBoxShadows, false),
+1 −1
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ class PipDesktopState(
    private val pipDisplayLayoutState: PipDisplayLayoutState,
    private val desktopUserRepositoriesOptional: Optional<DesktopUserRepositories>,
    private val dragToDesktopTransitionHandlerOptional: Optional<DragToDesktopTransitionHandler>,
    private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
    val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
) {
    /**
     * Returns whether PiP in Desktop Windowing is enabled by checking the following:
+3 −2
Original line number Diff line number Diff line
@@ -153,9 +153,10 @@ public abstract class Pip2Module {
            @ShellMainThread ShellExecutor mainExecutor,
            PipTransitionState pipTransitionState,
            Optional<SplitScreenController> splitScreenControllerOptional,
            PipDesktopState pipDesktopState) {
            PipDesktopState pipDesktopState,
            DisplayController displayController) {
        return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState,
                splitScreenControllerOptional, pipDesktopState);
                splitScreenControllerOptional, pipDesktopState, displayController);
    }

    @WMSingleton
+91 −0
Original line number Diff line number Diff line
/*
 * 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.pip2.phone;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.SurfaceControl;

import androidx.annotation.VisibleForTesting;

/**
 * Handler for moving PiP window to another display when the device is connected to external
 * display(s) in extended mode.
 */
public class PipDisplayTransferHandler implements
        PipTransitionState.PipTransitionStateChangedListener {

    private static final String TAG = "PipDisplayTransferHandler";
    static final String ORIGIN_DISPLAY_ID_KEY = "origin_display_id";
    static final String TARGET_DISPLAY_ID_KEY = "target_display_id";

    @NonNull private final PipTransitionState mPipTransitionState;
    @NonNull private final PipScheduler mPipScheduler;
    @VisibleForTesting boolean mWaitingForDisplayTransfer;

    public PipDisplayTransferHandler(PipTransitionState pipTransitionState,
            PipScheduler pipScheduler) {
        mPipTransitionState = pipTransitionState;
        mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
        mPipScheduler = pipScheduler;
    }

    void scheduleMovePipToDisplay(int originDisplayId, int targetDisplayId) {
        Bundle extra = new Bundle();
        extra.putInt(ORIGIN_DISPLAY_ID_KEY, originDisplayId);
        extra.putInt(TARGET_DISPLAY_ID_KEY, targetDisplayId);

        mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
    }

    @Override
    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
            @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
        switch (newState) {
            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
                if (extra == null || !extra.containsKey(ORIGIN_DISPLAY_ID_KEY)
                        || !extra.containsKey(TARGET_DISPLAY_ID_KEY)) {
                    break;
                }
                mWaitingForDisplayTransfer = true;

                mPipScheduler.scheduleMoveToDisplay(extra.getInt(ORIGIN_DISPLAY_ID_KEY),
                        extra.getInt(TARGET_DISPLAY_ID_KEY));
                break;
            case PipTransitionState.CHANGING_PIP_BOUNDS:
                if (extra == null || !mWaitingForDisplayTransfer) {
                    break;
                }

                final SurfaceControl.Transaction startTx = extra.getParcelable(
                        PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
                final Rect destinationBounds = extra.getParcelable(
                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);

                startMoveToDisplayAnimation(startTx, destinationBounds);
        }
    }

    private void startMoveToDisplayAnimation(SurfaceControl.Transaction startTx,
            Rect destinationBounds) {
        if (startTx == null) return;

        startTx.apply();
        mPipScheduler.scheduleFinishResizePip(destinationBounds);
    }
}
+41 −2
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.wm.shell.pip2.phone;

import static android.hardware.display.DisplayTopology.dpToPx;
import static android.hardware.display.DisplayTopology.pxToDp;

import android.app.PictureInPictureParams;
import android.content.Context;
import android.graphics.Matrix;
@@ -23,6 +26,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.SystemProperties;
import android.view.SurfaceControl;
import android.window.DisplayAreaInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

@@ -31,6 +35,7 @@ import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -59,11 +64,13 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
            SystemProperties.getInt(
                    "persist.wm.debug.extra_content_overlay_fade_out_delay_ms", 400);
    private static final int CONTENT_OVERLAY_FADE_OUT_DURATION_MS = 500;
    private static final int DISPLAY_TRANSFER_DURATION_MS = 250;

    private final Context mContext;
    private final PipBoundsState mPipBoundsState;
    private final ShellExecutor mMainExecutor;
    private final PipTransitionState mPipTransitionState;
    private final DisplayController mDisplayController;
    private final PipDesktopState mPipDesktopState;
    private final Optional<SplitScreenController> mSplitScreenControllerOptional;
    private PipTransitionController mPipTransitionController;
@@ -82,7 +89,8 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
            ShellExecutor mainExecutor,
            PipTransitionState pipTransitionState,
            Optional<SplitScreenController> splitScreenControllerOptional,
            PipDesktopState pipDesktopState) {
            PipDesktopState pipDesktopState,
            DisplayController displayController) {
        mContext = context;
        mPipBoundsState = pipBoundsState;
        mMainExecutor = mainExecutor;
@@ -90,7 +98,7 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
        mPipTransitionState.addPipTransitionStateChangedListener(this);
        mPipDesktopState = pipDesktopState;
        mSplitScreenControllerOptional = splitScreenControllerOptional;

        mDisplayController = displayController;
        mSurfaceControlTransactionFactory =
                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
        mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(mContext);
@@ -194,6 +202,37 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange
        mPipTransitionController.startResizeTransition(wct, duration);
    }

    /**
     * Schedules moving PiP window to another display.
     *
     * @param originDisplayId the origin display ID where the PiP window was dragged from.
     * @param targetDisplayId the target display ID where the PiP window should be parented to.
     */
    public void scheduleMoveToDisplay(int originDisplayId, int targetDisplayId) {
        WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken();
        DisplayAreaInfo displayAreaInfo =
                mPipDesktopState.getRootTaskDisplayAreaOrganizer().getDisplayAreaInfo(
                        targetDisplayId);
        if (pipTaskToken == null || !mPipTransitionState.isInPip() || displayAreaInfo == null) {
            return;
        }

        WindowContainerTransaction wct = new WindowContainerTransaction();
        WindowContainerToken displayToken = displayAreaInfo.token;
        final int originDisplayDpi = mDisplayController.getDisplayLayout(
                originDisplayId).densityDpi();
        final int targetDisplayDpi = mDisplayController.getDisplayLayout(
                targetDisplayId).densityDpi();
        Rect pipBounds = mPipBoundsState.getBounds();
        float newWidth = dpToPx(pxToDp(pipBounds.width(), originDisplayDpi), targetDisplayDpi);
        float newHeight = dpToPx(pxToDp(pipBounds.height(), originDisplayDpi), targetDisplayDpi);
        wct.reparent(pipTaskToken, displayToken, /* onTop= */ true);
        Rect newSampleBounds = new Rect(0, 0, (int) newWidth, (int) newHeight);
        wct.setBounds(pipTaskToken, newSampleBounds);

        mPipTransitionController.startResizeTransition(wct, DISPLAY_TRANSFER_DURATION_MS);
    }

    /**
     * Signals to Core to finish the PiP resize transition.
     * Note that we do not allow any actual WM Core changes at this point.
Loading