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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user