Merge "Move PiP window upon tabletop mode changes" into tm-qpr-dev
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user