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

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21734145

Change-Id: If42dcc6f7c1ac224b3b93f992c8426d50c6aa18d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Hongwei Wang
2023-03-14 23:26:15 +00:00
committed by Automerger Merge Worker
5 changed files with 118 additions and 5 deletions

View File

@@ -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.common.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FOLDABLE; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FOLDABLE;
import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.WindowConfiguration; import android.app.WindowConfiguration;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.SystemProperties;
import android.util.ArraySet; import android.util.ArraySet;
import android.view.Surface; 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.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.sysui.ShellInit;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -49,8 +53,34 @@ import java.util.Set;
public class TabletopModeController implements public class TabletopModeController implements
DevicePostureController.OnDevicePostureChangedListener, DevicePostureController.OnDevicePostureChangedListener,
DisplayController.OnDisplaysChangedListener { 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; 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 Context mContext;
private final DevicePostureController mDevicePostureController; 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. */ /** Register {@link OnTabletopModeChangedListener} to listen for tabletop mode change. */
public void registerOnTabletopModeChangedListener( public void registerOnTabletopModeChangedListener(
@NonNull OnTabletopModeChangedListener listener) { @NonNull OnTabletopModeChangedListener listener) {

View File

@@ -44,6 +44,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows; 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.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellBackgroundThread;
@@ -353,6 +354,7 @@ public abstract class WMShellModule {
TaskStackListenerImpl taskStackListener, TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder, PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayInsetsController displayInsetsController, DisplayInsetsController displayInsetsController,
TabletopModeController pipTabletopController,
Optional<OneHandedController> oneHandedController, Optional<OneHandedController> oneHandedController,
@ShellMainThread ShellExecutor mainExecutor) { @ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create( return Optional.ofNullable(PipController.create(
@@ -362,7 +364,7 @@ public abstract class WMShellModule {
pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
pipTouchHandler, pipTransitionController, windowManagerShellWrapper, pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
taskStackListener, pipParamsChangedForwarder, displayInsetsController, taskStackListener, pipParamsChangedForwarder, displayInsetsController,
oneHandedController, mainExecutor)); pipTabletopController, oneHandedController, mainExecutor));
} }
@WMSingleton @WMSingleton

View File

@@ -44,7 +44,9 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -115,6 +117,12 @@ public class PipBoundsState {
* @see android.view.View#setPreferKeepClearRects * @see android.view.View#setPreferKeepClearRects
*/ */
private final Set<Rect> mUnrestrictedKeepClearAreas = new ArraySet<>(); 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 Runnable mOnMinimalSizeChangeCallback;
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback; private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
@@ -393,6 +401,16 @@ public class PipBoundsState {
mUnrestrictedKeepClearAreas.addAll(unrestrictedAreas); 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 @NonNull
public Set<Rect> getRestrictedKeepClearAreas() { public Set<Rect> getRestrictedKeepClearAreas() {
return mRestrictedKeepClearAreas; return mRestrictedKeepClearAreas;
@@ -400,7 +418,10 @@ public class PipBoundsState {
@NonNull @NonNull
public Set<Rect> getUnrestrictedKeepClearAreas() { public Set<Rect> getUnrestrictedKeepClearAreas() {
return mUnrestrictedKeepClearAreas; if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
final Set<Rect> unrestrictedAreas = new ArraySet<>(mUnrestrictedKeepClearAreas);
unrestrictedAreas.addAll(mNamedUnrestrictedKeepClearAreas.values());
return unrestrictedAreas;
} }
/** /**

View File

@@ -43,6 +43,7 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.SystemProperties; 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.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener; 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.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedController;
@@ -145,6 +147,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private TaskStackListenerImpl mTaskStackListener; private TaskStackListenerImpl mTaskStackListener;
private PipParamsChangedForwarder mPipParamsChangedForwarder; private PipParamsChangedForwarder mPipParamsChangedForwarder;
private DisplayInsetsController mDisplayInsetsController; private DisplayInsetsController mDisplayInsetsController;
private TabletopModeController mTabletopModeController;
private Optional<OneHandedController> mOneHandedController; private Optional<OneHandedController> mOneHandedController;
private final ShellCommandHandler mShellCommandHandler; private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController; private final ShellController mShellController;
@@ -400,6 +403,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
TaskStackListenerImpl taskStackListener, TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder, PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayInsetsController displayInsetsController, DisplayInsetsController displayInsetsController,
TabletopModeController pipTabletopController,
Optional<OneHandedController> oneHandedController, Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor) { ShellExecutor mainExecutor) {
if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
@@ -414,7 +418,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
pipTransitionState, pipTouchHandler, pipTransitionController, pipTransitionState, pipTouchHandler, pipTransitionController,
windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder, windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
displayInsetsController, oneHandedController, mainExecutor) displayInsetsController, pipTabletopController, oneHandedController, mainExecutor)
.mImpl; .mImpl;
} }
@@ -440,6 +444,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
TaskStackListenerImpl taskStackListener, TaskStackListenerImpl taskStackListener,
PipParamsChangedForwarder pipParamsChangedForwarder, PipParamsChangedForwarder pipParamsChangedForwarder,
DisplayInsetsController displayInsetsController, DisplayInsetsController displayInsetsController,
TabletopModeController tabletopModeController,
Optional<OneHandedController> oneHandedController, Optional<OneHandedController> oneHandedController,
ShellExecutor mainExecutor ShellExecutor mainExecutor
) { ) {
@@ -472,6 +477,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
.getInteger(R.integer.config_pipEnterAnimationDuration); .getInteger(R.integer.config_pipEnterAnimationDuration);
mPipParamsChangedForwarder = pipParamsChangedForwarder; mPipParamsChangedForwarder = pipParamsChangedForwarder;
mDisplayInsetsController = displayInsetsController; mDisplayInsetsController = displayInsetsController;
mTabletopModeController = tabletopModeController;
shellInit.addInitCallback(this::onInit, this); 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 -> { mOneHandedController.ifPresent(controller -> {
controller.registerTransitionCallback( controller.registerTransitionCallback(
new OneHandedTransitionCallback() { new OneHandedTransitionCallback() {

View File

@@ -53,6 +53,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor; 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.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipAnimationController;
@@ -113,6 +114,7 @@ public class PipControllerTest extends ShellTestCase {
@Mock private Optional<OneHandedController> mMockOneHandedController; @Mock private Optional<OneHandedController> mMockOneHandedController;
@Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder; @Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder;
@Mock private DisplayInsetsController mMockDisplayInsetsController; @Mock private DisplayInsetsController mMockDisplayInsetsController;
@Mock private TabletopModeController mMockTabletopModeController;
@Mock private DisplayLayout mMockDisplayLayout1; @Mock private DisplayLayout mMockDisplayLayout1;
@Mock private DisplayLayout mMockDisplayLayout2; @Mock private DisplayLayout mMockDisplayLayout2;
@@ -135,7 +137,7 @@ public class PipControllerTest extends ShellTestCase {
mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController, mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
mMockWindowManagerShellWrapper, mMockTaskStackListener, mMockWindowManagerShellWrapper, mMockTaskStackListener,
mMockPipParamsChangedForwarder, mMockDisplayInsetsController, mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
mMockOneHandedController, mMockExecutor); mMockTabletopModeController, mMockOneHandedController, mMockExecutor);
mShellInit.init(); mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm); when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper); when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -226,7 +228,7 @@ public class PipControllerTest extends ShellTestCase {
mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController, mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
mMockWindowManagerShellWrapper, mMockTaskStackListener, mMockWindowManagerShellWrapper, mMockTaskStackListener,
mMockPipParamsChangedForwarder, mMockDisplayInsetsController, mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
mMockOneHandedController, mMockExecutor)); mMockTabletopModeController, mMockOneHandedController, mMockExecutor));
} }
@Test @Test