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

Commit f542109b authored by Hongwei Wang's avatar Hongwei Wang Committed by Android (Google) Code Review
Browse files

Merge "Move PiP window upon tabletop mode changes" into tm-qpr-dev

parents 9b98c8e9 38cb69d2
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -22,10 +22,12 @@ import static com.android.wm.shell.common.DevicePostureController.DEVICE_POSTURE
import static com.android.wm.shell.common.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FOLDABLE;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.res.Configuration;
import android.os.SystemProperties;
import android.util.ArraySet;
import android.view.Surface;

@@ -34,6 +36,8 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -49,8 +53,34 @@ import java.util.Set;
public class TabletopModeController implements
        DevicePostureController.OnDevicePostureChangedListener,
        DisplayController.OnDisplaysChangedListener {
    /**
     * When {@code true}, floating windows like PiP would auto move to the position
     * specified by {@link #PREFER_TOP_HALF_IN_TABLETOP} when in tabletop mode.
     */
    private static final boolean ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP =
            SystemProperties.getBoolean(
                    "persist.wm.debug.enable_move_floating_window_in_tabletop", false);

    /**
     * Prefer the {@link #PREFERRED_TABLETOP_HALF_TOP} if this flag is enabled,
     * {@link #PREFERRED_TABLETOP_HALF_BOTTOM} otherwise.
     * See also {@link #getPreferredHalfInTabletopMode()}.
     */
    private static final boolean PREFER_TOP_HALF_IN_TABLETOP =
            SystemProperties.getBoolean("persist.wm.debug.prefer_top_half_in_tabletop", true);

    private static final long TABLETOP_MODE_DELAY_MILLIS = 1_000;

    @IntDef(prefix = {"PREFERRED_TABLETOP_HALF_"}, value = {
            PREFERRED_TABLETOP_HALF_TOP,
            PREFERRED_TABLETOP_HALF_BOTTOM
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface PreferredTabletopHalf {}

    public static final int PREFERRED_TABLETOP_HALF_TOP = 0;
    public static final int PREFERRED_TABLETOP_HALF_BOTTOM = 1;

    private final Context mContext;

    private final DevicePostureController mDevicePostureController;
@@ -132,6 +162,22 @@ public class TabletopModeController implements
        }
    }

    /**
     * @return {@code true} if floating windows like PiP would auto move to the position
     * specified by {@link #getPreferredHalfInTabletopMode()} when in tabletop mode.
     */
    public boolean enableMoveFloatingWindowInTabletop() {
        return ENABLE_MOVE_FLOATING_WINDOW_IN_TABLETOP;
    }

    /** @return Preferred half for floating windows like PiP when in tabletop mode. */
    @PreferredTabletopHalf
    public int getPreferredHalfInTabletopMode() {
        return PREFER_TOP_HALF_IN_TABLETOP
                ? PREFERRED_TABLETOP_HALF_TOP
                : PREFERRED_TABLETOP_HALF_BOTTOM;
    }

    /** Register {@link OnTabletopModeChangedListener} to listen for tabletop mode change. */
    public void registerOnTabletopModeChangedListener(
            @NonNull OnTabletopModeChangedListener listener) {
+3 −1
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
@@ -353,6 +354,7 @@ public abstract class WMShellModule {
            TaskStackListenerImpl taskStackListener,
            PipParamsChangedForwarder pipParamsChangedForwarder,
            DisplayInsetsController displayInsetsController,
            TabletopModeController pipTabletopController,
            Optional<OneHandedController> oneHandedController,
            @ShellMainThread ShellExecutor mainExecutor) {
        return Optional.ofNullable(PipController.create(
@@ -362,7 +364,7 @@ public abstract class WMShellModule {
                pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
                pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
                taskStackListener, pipParamsChangedForwarder, displayInsetsController,
                oneHandedController, mainExecutor));
                pipTabletopController, oneHandedController, mainExecutor));
    }

    @WMSingleton
+22 −1
Original line number Diff line number Diff line
@@ -44,7 +44,9 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
@@ -115,6 +117,12 @@ public class PipBoundsState {
     * @see android.view.View#setPreferKeepClearRects
     */
    private final Set<Rect> mUnrestrictedKeepClearAreas = new ArraySet<>();
    /**
     * Additional to {@link #mUnrestrictedKeepClearAreas}, allow the caller to append named bounds
     * as unrestricted keep clear area. Values in this map would be appended to
     * {@link #getUnrestrictedKeepClearAreas()} and this is meant for internal usage only.
     */
    private final Map<String, Rect> mNamedUnrestrictedKeepClearAreas = new HashMap<>();

    private @Nullable Runnable mOnMinimalSizeChangeCallback;
    private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
@@ -393,6 +401,16 @@ public class PipBoundsState {
        mUnrestrictedKeepClearAreas.addAll(unrestrictedAreas);
    }

    /** Add a named unrestricted keep clear area. */
    public void addNamedUnrestrictedKeepClearArea(@NonNull String name, Rect unrestrictedArea) {
        mNamedUnrestrictedKeepClearAreas.put(name, unrestrictedArea);
    }

    /** Remove a named unrestricted keep clear area. */
    public void removeNamedUnrestrictedKeepClearArea(@NonNull String name) {
        mNamedUnrestrictedKeepClearAreas.remove(name);
    }

    @NonNull
    public Set<Rect> getRestrictedKeepClearAreas() {
        return mRestrictedKeepClearAreas;
@@ -400,7 +418,10 @@ public class PipBoundsState {

    @NonNull
    public Set<Rect> getUnrestrictedKeepClearAreas() {
        return mUnrestrictedKeepClearAreas;
        if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
        final Set<Rect> unrestrictedAreas = new ArraySet<>(mUnrestrictedKeepClearAreas);
        unrestrictedAreas.addAll(mNamedUnrestrictedKeepClearAreas.values());
        return unrestrictedAreas;
    }

    /**
+43 −1
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -71,6 +72,7 @@ import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController;
@@ -145,6 +147,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
    private TaskStackListenerImpl mTaskStackListener;
    private PipParamsChangedForwarder mPipParamsChangedForwarder;
    private DisplayInsetsController mDisplayInsetsController;
    private TabletopModeController mTabletopModeController;
    private Optional<OneHandedController> mOneHandedController;
    private final ShellCommandHandler mShellCommandHandler;
    private final ShellController mShellController;
@@ -400,6 +403,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
            TaskStackListenerImpl taskStackListener,
            PipParamsChangedForwarder pipParamsChangedForwarder,
            DisplayInsetsController displayInsetsController,
            TabletopModeController pipTabletopController,
            Optional<OneHandedController> oneHandedController,
            ShellExecutor mainExecutor) {
        if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
@@ -414,7 +418,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
                pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
                pipTransitionState, pipTouchHandler, pipTransitionController,
                windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
                displayInsetsController, oneHandedController, mainExecutor)
                displayInsetsController, pipTabletopController, oneHandedController, mainExecutor)
                .mImpl;
    }

@@ -440,6 +444,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
            TaskStackListenerImpl taskStackListener,
            PipParamsChangedForwarder pipParamsChangedForwarder,
            DisplayInsetsController displayInsetsController,
            TabletopModeController tabletopModeController,
            Optional<OneHandedController> oneHandedController,
            ShellExecutor mainExecutor
    ) {
@@ -472,6 +477,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
                .getInteger(R.integer.config_pipEnterAnimationDuration);
        mPipParamsChangedForwarder = pipParamsChangedForwarder;
        mDisplayInsetsController = displayInsetsController;
        mTabletopModeController = tabletopModeController;

        shellInit.addInitCallback(this::onInit, this);
    }
@@ -655,6 +661,42 @@ public class PipController implements PipTransitionController.PipTransitionCallb
                    }
                });

        mTabletopModeController.registerOnTabletopModeChangedListener((isInTabletopMode) -> {
            if (!mTabletopModeController.enableMoveFloatingWindowInTabletop()) return;
            final String tag = "tabletop-mode";
            if (!isInTabletopMode) {
                mPipBoundsState.removeNamedUnrestrictedKeepClearArea(tag);
                return;
            }

            // To prepare for the entry bounds.
            final Rect displayBounds = mPipBoundsState.getDisplayBounds();
            if (mTabletopModeController.getPreferredHalfInTabletopMode()
                    == TabletopModeController.PREFERRED_TABLETOP_HALF_TOP) {
                // Prefer top, avoid the bottom half of the display.
                mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
                        displayBounds.left, displayBounds.centerY(),
                        displayBounds.right, displayBounds.bottom));
            } else {
                // Prefer bottom, avoid the top half of the display.
                mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
                        displayBounds.left, displayBounds.top,
                        displayBounds.right, displayBounds.centerY()));
            }

            // Try to move the PiP window if we have entered PiP mode.
            if (mPipTransitionState.hasEnteredPip()) {
                final Rect pipBounds = mPipBoundsState.getBounds();
                final Point edgeInsets = mPipSizeSpecHandler.getScreenEdgeInsets();
                if ((pipBounds.height() + 2 * edgeInsets.y) > (displayBounds.height() / 2)) {
                    // PiP bounds is too big to fit either half, bail early.
                    return;
                }
                mMainExecutor.removeCallbacks(mMovePipInResponseToKeepClearAreasChangeCallback);
                mMainExecutor.execute(mMovePipInResponseToKeepClearAreasChangeCallback);
            }
        });

        mOneHandedController.ifPresent(controller -> {
            controller.registerTransitionCallback(
                    new OneHandedTransitionCallback() {
+4 −2
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController;
@@ -113,6 +114,7 @@ public class PipControllerTest extends ShellTestCase {
    @Mock private Optional<OneHandedController> mMockOneHandedController;
    @Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder;
    @Mock private DisplayInsetsController mMockDisplayInsetsController;
    @Mock private TabletopModeController mMockTabletopModeController;

    @Mock private DisplayLayout mMockDisplayLayout1;
    @Mock private DisplayLayout mMockDisplayLayout2;
@@ -135,7 +137,7 @@ public class PipControllerTest extends ShellTestCase {
                mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
                mMockWindowManagerShellWrapper, mMockTaskStackListener,
                mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
                mMockOneHandedController, mMockExecutor);
                mMockTabletopModeController, mMockOneHandedController, mMockExecutor);
        mShellInit.init();
        when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
        when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -226,7 +228,7 @@ public class PipControllerTest extends ShellTestCase {
                mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
                mMockWindowManagerShellWrapper, mMockTaskStackListener,
                mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
                mMockOneHandedController, mMockExecutor));
                mMockTabletopModeController, mMockOneHandedController, mMockExecutor));
    }

    @Test