Merge "Revert^2 "PiP: support direct entering split from PiP."" into sc-v2-dev

This commit is contained in:
TreeHugger Robot
2021-11-11 17:55:21 +00:00
committed by Android (Google) Code Review
16 changed files with 172 additions and 73 deletions

View File

@@ -65,25 +65,28 @@
<LinearLayout <LinearLayout
android:id="@+id/top_end_container" android:id="@+id/top_end_container"
android:layout_gravity="top|end" android:layout_gravity="top|end"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageButton <ImageButton
android:id="@+id/settings" android:id="@+id/settings"
android:layout_width="@dimen/pip_action_size" android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size" android:layout_height="@dimen/pip_action_size"
android:contentDescription="@string/pip_phone_settings" android:contentDescription="@string/pip_phone_settings"
android:layout_gravity="top|start"
android:gravity="center" android:gravity="center"
android:src="@drawable/pip_ic_settings" android:src="@drawable/pip_ic_settings"
android:background="?android:selectableItemBackgroundBorderless" /> android:background="?android:selectableItemBackgroundBorderless" />
<ImageButton <ImageButton
android:id="@+id/dismiss" android:id="@+id/enter_split"
android:layout_width="@dimen/pip_action_size" android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size" android:layout_height="@dimen/pip_action_size"
android:contentDescription="@string/pip_phone_close" android:layout_gravity="top|start"
android:gravity="center" android:gravity="center"
android:src="@drawable/pip_ic_close_white" android:contentDescription="@string/pip_phone_enter_split"
android:src="@drawable/pip_expand"
android:background="?android:selectableItemBackgroundBorderless" /> android:background="?android:selectableItemBackgroundBorderless" />
</LinearLayout> </LinearLayout>
@@ -97,4 +100,14 @@
android:padding="@dimen/pip_resize_handle_padding" android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle" android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" /> android:background="?android:selectableItemBackgroundBorderless" />
<ImageButton
android:id="@+id/dismiss"
android:layout_width="@dimen/pip_action_size"
android:layout_height="@dimen/pip_action_size"
android:contentDescription="@string/pip_phone_close"
android:layout_gravity="top|end"
android:gravity="center"
android:src="@drawable/pip_ic_close_white"
android:background="?android:selectableItemBackgroundBorderless" />
</FrameLayout> </FrameLayout>

View File

@@ -24,6 +24,9 @@
<!-- Label for PIP settings button [CHAR LIMIT=NONE]--> <!-- Label for PIP settings button [CHAR LIMIT=NONE]-->
<string name="pip_phone_settings">Settings</string> <string name="pip_phone_settings">Settings</string>
<!-- Label for the PIP enter split button [CHAR LIMIT=NONE] -->
<string name="pip_phone_enter_split">Enter split screen</string>
<!-- Title of menu shown over picture-in-picture. Used for accessibility. --> <!-- Title of menu shown over picture-in-picture. Used for accessibility. -->
<string name="pip_menu_title">Menu</string> <string name="pip_menu_title">Menu</string>

View File

@@ -43,6 +43,7 @@ import com.android.wm.shell.pip.tv.TvPipController;
import com.android.wm.shell.pip.tv.TvPipMenuController; import com.android.wm.shell.pip.tv.TvPipMenuController;
import com.android.wm.shell.pip.tv.TvPipNotificationController; import com.android.wm.shell.pip.tv.TvPipNotificationController;
import com.android.wm.shell.pip.tv.TvPipTransition; import com.android.wm.shell.pip.tv.TvPipTransition;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions;
import java.util.Optional; import java.util.Optional;
@@ -160,13 +161,14 @@ public abstract class TvPipModule {
PipTransitionController pipTransitionController, PipTransitionController pipTransitionController,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper, PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<LegacySplitScreenController> splitScreenOptional, Optional<LegacySplitScreenController> splitScreenOptional,
Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController, DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) { @ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm, syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper, tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, pipTransitionController, splitScreenOptional, newSplitScreenOptional,
shellTaskOrganizer, mainExecutor); displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
} }
} }

View File

@@ -55,6 +55,7 @@ import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipController; import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm; import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions;
@@ -215,14 +216,15 @@ public class WMShellModule {
PipSurfaceTransactionHelper pipSurfaceTransactionHelper, PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
PipTransitionController pipTransitionController, PipTransitionController pipTransitionController,
Optional<LegacySplitScreenController> splitScreenOptional, Optional<LegacySplitScreenController> splitScreenOptional,
Optional<SplitScreenController> newSplitScreenOptional,
DisplayController displayController, DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@ShellMainThread ShellExecutor mainExecutor) { @ShellMainThread ShellExecutor mainExecutor) {
return new PipTaskOrganizer(context, return new PipTaskOrganizer(context,
syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm, syncTransactionQueue, pipTransitionState, pipBoundsState, pipBoundsAlgorithm,
menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper, menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper,
pipTransitionController, splitScreenOptional, displayController, pipUiEventLogger, pipTransitionController, splitScreenOptional, newSplitScreenOptional,
shellTaskOrganizer, mainExecutor); displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor);
} }
@WMSingleton @WMSingleton

View File

@@ -77,6 +77,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter; import java.io.PrintWriter;
@@ -126,7 +127,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
private final int mExitAnimationDuration; private final int mExitAnimationDuration;
private final int mCrossFadeAnimationDuration; private final int mCrossFadeAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<LegacySplitScreenController> mSplitScreenOptional; private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor; protected final ShellExecutor mMainExecutor;
@@ -252,7 +254,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@NonNull PipAnimationController pipAnimationController, @NonNull PipAnimationController pipAnimationController,
@NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@NonNull PipTransitionController pipTransitionController, @NonNull PipTransitionController pipTransitionController,
Optional<LegacySplitScreenController> splitScreenOptional, Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
@NonNull DisplayController displayController, @NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger, @NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer, @NonNull ShellTaskOrganizer shellTaskOrganizer,
@@ -274,6 +277,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mPipAnimationController = pipAnimationController; mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger; mPipUiEventLoggerLogger = pipUiEventLogger;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
mLegacySplitScreenOptional = legacySplitScreenOptional;
mSplitScreenOptional = splitScreenOptional; mSplitScreenOptional = splitScreenOptional;
mTaskOrganizer = shellTaskOrganizer; mTaskOrganizer = shellTaskOrganizer;
mMainExecutor = mainExecutor; mMainExecutor = mainExecutor;
@@ -373,8 +377,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* activity render it's final configuration while the Task is still in PiP. * activity render it's final configuration while the Task is still in PiP.
* - setWindowingMode to undefined at the end of transition * - setWindowingMode to undefined at the end of transition
* @param animationDurationMs duration in millisecond for the exiting PiP transition * @param animationDurationMs duration in millisecond for the exiting PiP transition
* @param requestEnterSplit whether the enterSplit button is pressed on PiP or not.
* Indicate the user wishes to directly put PiP into split screen
* mode.
*/ */
public void exitPip(int animationDurationMs) { public void exitPip(int animationDurationMs, boolean requestEnterSplit) {
if (!mPipTransitionState.isInPip() if (!mPipTransitionState.isInPip()
|| mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP || mPipTransitionState.getTransitionState() == PipTransitionState.EXITING_PIP
|| mToken == null) { || mToken == null) {
@@ -387,7 +394,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN); PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect destinationBounds = mPipBoundsState.getDisplayBounds(); final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds) final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN ? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP; : TRANSITION_DIRECTION_LEAVE_PIP;
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
@@ -396,7 +403,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// We set to fullscreen here for now, but later it will be set to UNDEFINED for // We set to fullscreen here for now, but later it will be set to UNDEFINED for
// the proper windowing mode to take place. See #applyWindowingModeChangeOnExit. // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
wct.setActivityWindowingMode(mToken, wct.setActivityWindowingMode(mToken,
direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN && !requestEnterSplit
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_FULLSCREEN); : WINDOWING_MODE_FULLSCREEN);
wct.setBounds(mToken, destinationBounds); wct.setBounds(mToken, destinationBounds);
@@ -435,7 +442,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
wct.setWindowingMode(mToken, getOutPipWindowingMode()); wct.setWindowingMode(mToken, getOutPipWindowingMode());
// Simply reset the activity mode set prior to the animation running. // Simply reset the activity mode set prior to the animation running.
wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
mSplitScreenOptional.ifPresent(splitScreen -> { mLegacySplitScreenOptional.ifPresent(splitScreen -> {
if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) { if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */); wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
} }
@@ -1165,6 +1172,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@PipAnimationController.TransitionDirection int direction, @PipAnimationController.TransitionDirection int direction,
@PipAnimationController.AnimationType int type) { @PipAnimationController.AnimationType int type) {
final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds()); final Rect preResizeBounds = new Rect(mPipBoundsState.getBounds());
final boolean isPipTopLeft = isPipTopLeft();
mPipBoundsState.setBounds(destinationBounds); mPipBoundsState.setBounds(destinationBounds);
if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
removePipImmediately(); removePipImmediately();
@@ -1210,10 +1218,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
null /* callback */, false /* withStartDelay */); null /* callback */, false /* withStartDelay */);
}); });
} else { } else {
applyFinishBoundsResize(wct, direction); applyFinishBoundsResize(wct, direction, isPipTopLeft);
} }
} else { } else {
applyFinishBoundsResize(wct, direction); applyFinishBoundsResize(wct, direction, isPipTopLeft);
} }
finishResizeForMenu(destinationBounds); finishResizeForMenu(destinationBounds);
@@ -1241,7 +1249,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
} else if (isOutPipDirection(direction)) { } else if (isOutPipDirection(direction)) {
// If we are animating to fullscreen or split screen, then we need to reset the // If we are animating to fullscreen or split screen, then we need to reset the
// override bounds on the task to ensure that the task "matches" the parent's bounds. // override bounds on the task to ensure that the task "matches" the parent's bounds.
taskBounds = null; if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
taskBounds = destinationBounds;
} else {
taskBounds = null;
}
applyWindowingModeChangeOnExit(wct, direction); applyWindowingModeChangeOnExit(wct, direction);
} else { } else {
// Just a resize in PIP // Just a resize in PIP
@@ -1261,8 +1273,20 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
* applying it. * applying it.
*/ */
public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct, public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
@PipAnimationController.TransitionDirection int direction) { @PipAnimationController.TransitionDirection int direction, boolean wasPipTopLeft) {
mTaskOrganizer.applyTransaction(wct); if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
mSplitScreenOptional.get().enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct);
} else {
mTaskOrganizer.applyTransaction(wct);
}
}
private boolean isPipTopLeft() {
final Rect topLeft = new Rect();
final Rect bottomRight = new Rect();
mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
return topLeft.contains(mPipBoundsState.getBounds());
} }
/** /**
@@ -1347,18 +1371,27 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
} }
/** /**
* Sync with {@link LegacySplitScreenController} on destination bounds if PiP is going to split * Sync with {@link LegacySplitScreenController} or {@link SplitScreenController} on destination
* screen. * bounds if PiP is going to split screen.
* *
* @param destinationBoundsOut contain the updated destination bounds if applicable * @param destinationBoundsOut contain the updated destination bounds if applicable
* @return {@code true} if destinationBounds is altered for split screen * @return {@code true} if destinationBounds is altered for split screen
*/ */
private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) { private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut, boolean enterSplit) {
if (!mSplitScreenOptional.isPresent()) { if (enterSplit && mSplitScreenOptional.isPresent()) {
final Rect topLeft = new Rect();
final Rect bottomRight = new Rect();
mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight);
final boolean isPipTopLeft = isPipTopLeft();
destinationBoundsOut.set(isPipTopLeft ? topLeft : bottomRight);
return true;
}
if (!mLegacySplitScreenOptional.isPresent()) {
return false; return false;
} }
LegacySplitScreenController legacySplitScreen = mSplitScreenOptional.get(); LegacySplitScreenController legacySplitScreen = mLegacySplitScreenOptional.get();
if (!legacySplitScreen.isDividerVisible()) { if (!legacySplitScreen.isDividerVisible()) {
// fail early if system is not in split screen mode // fail early if system is not in split screen mode
return false; return false;

View File

@@ -95,6 +95,11 @@ public class PhonePipMenuController implements PipMenuController {
* Called when the PIP requested to show the menu. * Called when the PIP requested to show the menu.
*/ */
void onPipShowMenu(); void onPipShowMenu();
/**
* Called when the PIP requested to enter Split.
*/
void onEnterSplit();
} }
private final Matrix mMoveTransform = new Matrix(); private final Matrix mMoveTransform = new Matrix();
@@ -458,6 +463,10 @@ public class PhonePipMenuController implements PipMenuController {
mListeners.forEach(Listener::onPipDismiss); mListeners.forEach(Listener::onPipDismiss);
} }
void onEnterSplit() {
mListeners.forEach(Listener::onEnterSplit);
}
/** /**
* @return the best set of actions to show in the PiP menu. * @return the best set of actions to show in the PiP menu.
*/ */

View File

@@ -151,7 +151,7 @@ public class PipAccessibilityInteractionConnection {
result = true; result = true;
break; break;
case AccessibilityNodeInfo.ACTION_EXPAND: case AccessibilityNodeInfo.ACTION_EXPAND:
mMotionHelper.expandLeavePip(); mMotionHelper.expandLeavePip(false /* skipAnimation */);
result = true; result = true;
break; break;
default: default:

View File

@@ -482,7 +482,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
false /* fromShelfAdjustment */, false /* fromShelfAdjustment */,
wct /* windowContainerTransaction */); wct /* windowContainerTransaction */);
if (wct != null) { if (wct != null) {
mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME); mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME,
false /* wasPipTopLeft */);
} }
}; };

View File

@@ -18,8 +18,6 @@ package com.android.wm.shell.pip.phone;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.Log;
import android.view.Gravity;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@@ -34,6 +32,7 @@ public class PipMenuIconsAlgorithm {
protected ViewGroup mViewRoot; protected ViewGroup mViewRoot;
protected ViewGroup mTopEndContainer; protected ViewGroup mTopEndContainer;
protected View mDragHandle; protected View mDragHandle;
protected View mEnterSplitButton;
protected View mSettingsButton; protected View mSettingsButton;
protected View mDismissButton; protected View mDismissButton;
@@ -44,14 +43,13 @@ public class PipMenuIconsAlgorithm {
* Bind the necessary views. * Bind the necessary views.
*/ */
public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle, public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle,
View settingsButton, View dismissButton) { View enterSplitButton, View settingsButton, View dismissButton) {
mViewRoot = viewRoot; mViewRoot = viewRoot;
mTopEndContainer = topEndContainer; mTopEndContainer = topEndContainer;
mDragHandle = dragHandle; mDragHandle = dragHandle;
mEnterSplitButton = enterSplitButton;
mSettingsButton = settingsButton; mSettingsButton = settingsButton;
mDismissButton = dismissButton; mDismissButton = dismissButton;
bindInitialViewState();
} }
/** /**
@@ -72,22 +70,4 @@ public class PipMenuIconsAlgorithm {
v.setLayoutParams(params); v.setLayoutParams(params);
} }
} }
/** Calculate the initial state of the menu icons. Called when the menu is first created. */
private void bindInitialViewState() {
if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
|| mSettingsButton == null || mDismissButton == null) {
Log.e(TAG, "One of the required views is null.");
return;
}
// The menu view layout starts out with the settings button aligned at the top|end of the
// view group next to the dismiss button. On phones, the settings button should be aligned
// to the top|start of the view, so move it to parent view group to then align it to the
// top|start of the menu.
mTopEndContainer.removeView(mSettingsButton);
mViewRoot.addView(mSettingsButton);
setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
}
} }

View File

@@ -99,7 +99,7 @@ public class PipMenuView extends FrameLayout {
private static final float MENU_BACKGROUND_ALPHA = 0.3f; private static final float MENU_BACKGROUND_ALPHA = 0.3f;
private static final float DISABLED_ACTION_ALPHA = 0.54f; private static final float DISABLED_ACTION_ALPHA = 0.54f;
private static final boolean ENABLE_RESIZE_HANDLE = false; private static final boolean ENABLE_ENTER_SPLIT = false;
private int mMenuState; private int mMenuState;
private boolean mAllowMenuTimeout = true; private boolean mAllowMenuTimeout = true;
@@ -139,7 +139,7 @@ public class PipMenuView extends FrameLayout {
protected View mViewRoot; protected View mViewRoot;
protected View mSettingsButton; protected View mSettingsButton;
protected View mDismissButton; protected View mDismissButton;
protected View mResizeHandle; protected View mEnterSplitButton;
protected View mTopEndContainer; protected View mTopEndContainer;
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
@@ -177,14 +177,23 @@ public class PipMenuView extends FrameLayout {
} }
}); });
mResizeHandle = findViewById(R.id.resize_handle); mEnterSplitButton = findViewById(R.id.enter_split);
mResizeHandle.setAlpha(0); mEnterSplitButton.setAlpha(0);
mEnterSplitButton.setOnClickListener(v -> {
if (mMenuContainer.getAlpha() != 0) {
enterSplit();
}
});
findViewById(R.id.resize_handle).setAlpha(0);
mActionsGroup = findViewById(R.id.actions_group); mActionsGroup = findViewById(R.id.actions_group);
mBetweenActionPaddingLand = getResources().getDimensionPixelSize( mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
R.dimen.pip_between_action_padding_land); R.dimen.pip_between_action_padding_land);
mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext); mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer, mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
mResizeHandle, mSettingsButton, mDismissButton); findViewById(R.id.resize_handle), mEnterSplitButton, mSettingsButton,
mDismissButton);
mDismissFadeOutDurationMs = context.getResources() mDismissFadeOutDurationMs = context.getResources()
.getInteger(R.integer.config_pipExitAnimationDuration); .getInteger(R.integer.config_pipExitAnimationDuration);
@@ -268,14 +277,13 @@ public class PipMenuView extends FrameLayout {
mSettingsButton.getAlpha(), 1f); mSettingsButton.getAlpha(), 1f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA, ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f); mDismissButton.getAlpha(), 1f);
ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA, ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
mResizeHandle.getAlpha(), mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
ENABLE_RESIZE_HANDLE && showResizeHandle ? 1f : 0f);
if (menuState == MENU_STATE_FULL) { if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
resizeAnim); enterSplitAnim);
} else { } else {
mMenuContainerAnimator.playTogether(resizeAnim); mMenuContainerAnimator.playTogether(enterSplitAnim);
} }
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN); mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS); mMenuContainerAnimator.setDuration(ANIMATION_HIDE_DURATION_MS);
@@ -328,7 +336,7 @@ public class PipMenuView extends FrameLayout {
mMenuContainer.setAlpha(0f); mMenuContainer.setAlpha(0f);
mSettingsButton.setAlpha(0f); mSettingsButton.setAlpha(0f);
mDismissButton.setAlpha(0f); mDismissButton.setAlpha(0f);
mResizeHandle.setAlpha(0f); mEnterSplitButton.setAlpha(0f);
} }
void pokeMenu() { void pokeMenu() {
@@ -368,9 +376,10 @@ public class PipMenuView extends FrameLayout {
mSettingsButton.getAlpha(), 0f); mSettingsButton.getAlpha(), 0f);
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA, ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 0f); mDismissButton.getAlpha(), 0f);
ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA, ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
mResizeHandle.getAlpha(), 0f); mEnterSplitButton.getAlpha(), 0f);
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim); mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
enterSplitAnim);
mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT); mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType)); mMenuContainerAnimator.setDuration(getFadeOutDuration(animationType));
mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() { mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
@@ -522,6 +531,14 @@ public class PipMenuView extends FrameLayout {
} }
} }
private void enterSplit() {
// Do not notify menu visibility when hiding the menu, the controller will do this when it
// handles the message
hideMenu(mController::onEnterSplit, false /* notifyMenuVisibility */, true /* resize */,
ANIM_TYPE_HIDE);
}
private void showSettings() { private void showSettings() {
final Pair<ComponentName, Integer> topPipActivityInfo = final Pair<ComponentName, Integer> topPipActivityInfo =
PipUtils.getTopPipActivity(mContext); PipUtils.getTopPipActivity(mContext);

View File

@@ -338,22 +338,29 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or * Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* * fullscreen depending on the display area's windowing mode. * * fullscreen depending on the display area's windowing mode.
*/ */
void expandLeavePip() { void expandLeavePip(boolean skipAnimation) {
expandLeavePip(false /* skipAnimation */); expandLeavePip(skipAnimation, false /* enterSplit */);
}
/**
* Resizes the pinned task to split-screen mode.
*/
void expandIntoSplit() {
expandLeavePip(false, true /* enterSplit */);
} }
/** /**
* Resizes the pinned stack back to unknown windowing mode, which could be freeform or * Resizes the pinned stack back to unknown windowing mode, which could be freeform or
* fullscreen depending on the display area's windowing mode. * fullscreen depending on the display area's windowing mode.
*/ */
void expandLeavePip(boolean skipAnimation) { private void expandLeavePip(boolean skipAnimation, boolean enterSplit) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
+ " callers=\n" + Debug.getCallers(5, " ")); + " callers=\n" + Debug.getCallers(5, " "));
} }
cancelPhysicsAnimation(); cancelPhysicsAnimation();
mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */); mMenuController.hideMenu(ANIM_TYPE_NONE, false /* resize */);
mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION); mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION, enterSplit);
} }
/** /**

View File

@@ -139,7 +139,12 @@ public class PipTouchHandler {
@Override @Override
public void onPipExpand() { public void onPipExpand() {
mMotionHelper.expandLeavePip(); mMotionHelper.expandLeavePip(false /* skipAnimation */);
}
@Override
public void onEnterSplit() {
mMotionHelper.expandIntoSplit();
} }
@Override @Override
@@ -899,7 +904,7 @@ public class PipTouchHandler {
// Expand to fullscreen if this is a double tap // Expand to fullscreen if this is a double tap
// the PiP should be frozen until the transition ends // the PiP should be frozen until the transition ends
setTouchEnabled(false); setTouchEnabled(false);
mMotionHelper.expandLeavePip(); mMotionHelper.expandLeavePip(false /* skipAnimation */);
} }
} else if (mMenuState != MENU_STATE_FULL) { } else if (mMenuState != MENU_STATE_FULL) {
if (mPipBoundsState.isStashed()) { if (mPipBoundsState.isStashed()) {

View File

@@ -219,7 +219,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
public void movePipToFullscreen() { public void movePipToFullscreen() {
if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState)); if (DEBUG) Log.d(TAG, "movePipToFullscreen(), state=" + stateToName(mState));
mPipTaskOrganizer.exitPip(mResizeAnimationDuration); mPipTaskOrganizer.exitPip(mResizeAnimationDuration, false /* requestEnterSplit */);
onPipDisappeared(); onPipDisappeared();
} }

View File

@@ -202,11 +202,25 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return moveToSideStage(task, sideStagePosition); return moveToSideStage(task, sideStagePosition);
} }
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
}
return moveToSideStage(task, sideStagePosition, wct);
}
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task, public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) { @SplitPosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition); return mStageCoordinator.moveToSideStage(task, sideStagePosition);
} }
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
}
public boolean removeFromSideStage(int taskId) { public boolean removeFromSideStage(int taskId) {
return mStageCoordinator.removeFromSideStage(taskId); return mStageCoordinator.removeFromSideStage(taskId);
} }
@@ -224,6 +238,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
} }
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
moveToSideStage(taskId,
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
}
public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) { public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason); mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason);
} }

View File

@@ -281,6 +281,11 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
boolean moveToSideStage(ActivityManager.RunningTaskInfo task, boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition) { @SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
return moveToSideStage(task, sideStagePosition, wct);
}
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction(); final WindowContainerTransaction evictWct = new WindowContainerTransaction();
setSideStagePosition(sideStagePosition, wct); setSideStagePosition(sideStagePosition, wct);
mSideStage.evictAllChildren(evictWct); mSideStage.evictAllChildren(evictWct);

View File

@@ -50,6 +50,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -75,7 +76,8 @@ public class PipTaskOrganizerTest extends ShellTestCase {
@Mock private PipTransitionController mMockPipTransitionController; @Mock private PipTransitionController mMockPipTransitionController;
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<LegacySplitScreenController> mMockOptionalSplitScreen; @Mock private Optional<LegacySplitScreenController> mMockOptionalLegacySplitScreen;
@Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
private TestShellExecutor mMainExecutor; private TestShellExecutor mMainExecutor;
private PipBoundsState mPipBoundsState; private PipBoundsState mPipBoundsState;
@@ -99,8 +101,9 @@ public class PipTaskOrganizerTest extends ShellTestCase {
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState, mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
mPipBoundsAlgorithm, mMockPhonePipMenuController, mPipBoundsAlgorithm, mMockPhonePipMenuController,
mMockPipAnimationController, mMockPipSurfaceTransactionHelper, mMockPipAnimationController, mMockPipSurfaceTransactionHelper,
mMockPipTransitionController, mMockOptionalSplitScreen, mMockDisplayController, mMockPipTransitionController, mMockOptionalLegacySplitScreen,
mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor)); mMockOptionalSplitScreen, mMockDisplayController, mMockPipUiEventLogger,
mMockShellTaskOrganizer, mMainExecutor));
mMainExecutor.flushAll(); mMainExecutor.flushAll();
preparePipTaskOrg(); preparePipTaskOrg();
} }