PiP: Show/hide enter split button based on focused task.

Bug: 202740657
Test: Enter PiP, go to home, tap on PiP - menu does not have split
button
Test: Enter PiP, open Messages, tap on PiP - menu shows split button

Change-Id: Ide755008f9b6ad844d7453fb024f2d6c7db4f36b
This commit is contained in:
Ben Lin
2021-11-11 16:53:02 -08:00
parent 35185d54b4
commit b26f380809
8 changed files with 115 additions and 5 deletions

View File

@@ -29,6 +29,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.Context;
import android.content.LocusId;
import android.content.pm.ActivityInfo;
@@ -121,6 +122,16 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
void onVisibilityChanged(int taskId, LocusId locus, boolean visible);
}
/**
* Callbacks for events in which the focus has changed.
*/
public interface FocusListener {
/**
* Notifies when the task which is focused has changed.
*/
void onFocusTaskChanged(RunningTaskInfo taskInfo);
}
/**
* Keys map from either a task id or {@link TaskListenerType}.
* @see #addListenerForTaskId
@@ -142,6 +153,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
/** @see #addLocusIdListener */
private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>();
private final ArraySet<FocusListener> mFocusListeners = new ArraySet<>();
private final Object mLock = new Object();
private StartingWindowController mStartingWindow;
@@ -155,6 +168,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
@Nullable
private final Optional<RecentTasksController> mRecentTasks;
@Nullable
private RunningTaskInfo mLastFocusedTaskInfo;
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
Optional.empty() /* recentTasksController */);
@@ -331,6 +347,27 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
}
}
/**
* Adds a listener to be notified for task focus changes.
*/
public void addFocusListener(FocusListener listener) {
synchronized (mLock) {
mFocusListeners.add(listener);
if (mLastFocusedTaskInfo != null) {
listener.onFocusTaskChanged(mLastFocusedTaskInfo);
}
}
}
/**
* Removes listener.
*/
public void removeLocusIdListener(FocusListener listener) {
synchronized (mLock) {
mFocusListeners.remove(listener);
}
}
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
if (mStartingWindow != null) {
@@ -422,6 +459,18 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
mRecentTasks.ifPresent(recentTasks ->
recentTasks.onTaskWindowingModeChanged(taskInfo));
}
// TODO (b/207687679): Remove check for HOME once bug is fixed
final boolean isFocusedOrHome = taskInfo.isFocused
|| (taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME
&& taskInfo.isVisible);
final boolean focusTaskChanged = (mLastFocusedTaskInfo == null
|| mLastFocusedTaskInfo.taskId != taskInfo.taskId) && isFocusedOrHome;
if (focusTaskChanged) {
for (int i = 0; i < mFocusListeners.size(); i++) {
mFocusListeners.valueAt(i).onFocusTaskChanged(taskInfo);
}
mLastFocusedTaskInfo = taskInfo;
}
}
}

View File

@@ -241,10 +241,11 @@ public class WMShellModule {
static PhonePipMenuController providesPipPhoneMenuController(Context context,
PipBoundsState pipBoundsState, PipMediaController pipMediaController,
SystemWindows systemWindows,
Optional<SplitScreenController> splitScreenOptional,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
systemWindows, mainExecutor, mainHandler);
systemWindows, splitScreenOptional, mainExecutor, mainHandler);
}
@WMSingleton

View File

@@ -89,6 +89,11 @@ public interface PipMenuController {
*/
default void updateMenuBounds(Rect destinationBounds) {}
/**
* Update when the current focused task changes.
*/
default void onFocusTaskChanged(RunningTaskInfo taskInfo) {}
/**
* Returns a default LayoutParams for the PIP Menu.
* @param width the PIP stack width.

View File

@@ -98,7 +98,7 @@ import java.util.function.IntConsumer;
* see also {@link PipMotionHelper}.
*/
public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
DisplayController.OnDisplaysChangedListener {
DisplayController.OnDisplaysChangedListener, ShellTaskOrganizer.FocusListener {
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
private static final boolean DEBUG = false;
/**
@@ -286,6 +286,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mMainExecutor.execute(() -> {
mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP);
});
mTaskOrganizer.addFocusListener(this);
mPipTransitionController.setPipOrganizer(this);
displayController.addDisplayWindowListener(this);
}
@@ -771,6 +772,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
null /* updateBoundsCallback */);
}
@Override
public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
mPipMenuController.onFocusTaskChanged(taskInfo);
}
@Override
public boolean supportSizeCompatUI() {
// PIP doesn't support size compat.

View File

@@ -19,6 +19,7 @@ package com.android.wm.shell.pip.phone;
import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.RemoteAction;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -43,10 +44,12 @@ import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Manages the PiP menu view which can show menu options or a scrim.
@@ -114,6 +117,7 @@ public class PhonePipMenuController implements PipMenuController {
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final SystemWindows mSystemWindows;
private final Optional<SplitScreenController> mSplitScreenController;
private ParceledListSlice<RemoteAction> mAppActions;
private ParceledListSlice<RemoteAction> mMediaActions;
private SyncRtSurfaceTransactionApplier mApplier;
@@ -145,6 +149,7 @@ public class PhonePipMenuController implements PipMenuController {
public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
PipMediaController mediaController, SystemWindows systemWindows,
Optional<SplitScreenController> splitScreenOptional,
ShellExecutor mainExecutor, Handler mainHandler) {
mContext = context;
mPipBoundsState = pipBoundsState;
@@ -152,6 +157,7 @@ public class PhonePipMenuController implements PipMenuController {
mSystemWindows = systemWindows;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
mSplitScreenController = splitScreenOptional;
}
public boolean isMenuVisible() {
@@ -180,7 +186,8 @@ public class PhonePipMenuController implements PipMenuController {
if (mPipMenuView != null) {
detachPipMenuView();
}
mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler);
mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler,
mSplitScreenController);
mSystemWindows.addView(mPipMenuView,
getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
0, SHELL_ROOT_LAYER_PIP);
@@ -209,6 +216,13 @@ public class PhonePipMenuController implements PipMenuController {
updateMenuLayout(destinationBounds);
}
@Override
public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
if (mPipMenuView != null) {
mPipMenuView.onFocusTaskChanged(taskInfo);
}
}
/**
* Tries to grab a surface control from {@link PipMenuView}. If this isn't available for some
* reason (ie. the window isn't ready yet, thus {@link android.view.ViewRootImpl} is

View File

@@ -16,6 +16,7 @@
package com.android.wm.shell.pip.phone;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
@@ -32,8 +33,10 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.PendingIntent.CanceledException;
import android.app.RemoteAction;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -61,11 +64,13 @@ import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Translucent window that gets started on top of a task in PIP to allow the user to control it.
@@ -105,6 +110,7 @@ public class PipMenuView extends FrameLayout {
private boolean mAllowMenuTimeout = true;
private boolean mAllowTouches = true;
private int mDismissFadeOutDurationMs;
private boolean mFocusedTaskAllowSplitScreen;
private final List<RemoteAction> mActions = new ArrayList<>();
@@ -116,6 +122,7 @@ public class PipMenuView extends FrameLayout {
private AnimatorSet mMenuContainerAnimator;
private PhonePipMenuController mController;
private Optional<SplitScreenController> mSplitScreenControllerOptional;
private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -144,12 +151,14 @@ public class PipMenuView extends FrameLayout {
protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
public PipMenuView(Context context, PhonePipMenuController controller,
ShellExecutor mainExecutor, Handler mainHandler) {
ShellExecutor mainExecutor, Handler mainHandler,
Optional<SplitScreenController> splitScreenController) {
super(context, null, 0);
mContext = context;
mController = controller;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
mSplitScreenControllerOptional = splitScreenController;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
inflate(context, R.layout.pip_menu, this);
@@ -255,6 +264,15 @@ public class PipMenuView extends FrameLayout {
return super.dispatchGenericMotionEvent(event);
}
public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
final boolean isSplitScreen = mSplitScreenControllerOptional.isPresent()
&& mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId);
mFocusedTaskAllowSplitScreen = isSplitScreen
|| (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
&& taskInfo.supportsSplitScreenMultiWindow
&& taskInfo.topActivityType != WindowConfiguration.ACTIVITY_TYPE_HOME);
}
void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
mAllowMenuTimeout = allowMenuTimeout;
@@ -278,7 +296,8 @@ public class PipMenuView extends FrameLayout {
ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
mDismissButton.getAlpha(), 1f);
ObjectAnimator enterSplitAnim = ObjectAnimator.ofFloat(mEnterSplitButton, View.ALPHA,
mEnterSplitButton.getAlpha(), ENABLE_ENTER_SPLIT ? 1f : 0f);
mEnterSplitButton.getAlpha(),
ENABLE_ENTER_SPLIT && mFocusedTaskAllowSplitScreen ? 1f : 0f);
if (menuState == MENU_STATE_FULL) {
mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
enterSplitAnim);

View File

@@ -183,6 +183,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return mStageCoordinator.isSplitScreenVisible();
}
public boolean isTaskInSplitScreen(int taskId) {
return isSplitScreenVisible()
&& mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED;
}
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
new WindowContainerTransaction());

View File

@@ -274,6 +274,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return mSideStageListener.mVisible && mMainStageListener.mVisible;
}
@SplitScreen.StageType
int getStageOfTask(int taskId) {
if (mMainStage.containsTask(taskId)) {
return STAGE_TYPE_MAIN;
} else if (mSideStage.containsTask(taskId)) {
return STAGE_TYPE_SIDE;
}
return STAGE_TYPE_UNDEFINED;
}
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitScreen.StageType int stageType,
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
StageTaskListener targetStage;