*/
@Override
public void enterPictureInPictureModeIfPossible() {
- if (mActivityInfo.resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE) {
+ if (mActivityInfo.supportsPictureInPicture()) {
enterPictureInPictureMode();
}
}
* True if the task can go in the docked stack.
* @hide
*/
- public boolean isDockable;
+ public boolean supportsSplitScreenMultiWindow;
/**
* The resize mode of the task. See {@link ActivityInfo#resizeMode}.
} else {
dest.writeInt(0);
}
- dest.writeInt(isDockable ? 1 : 0);
+ dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
dest.writeInt(resizeMode);
}
numActivities = source.readInt();
bounds = source.readInt() > 0 ?
Rect.CREATOR.createFromParcel(source) : null;
- isDockable = source.readInt() == 1;
+ supportsSplitScreenMultiWindow = source.readInt() == 1;
resizeMode = source.readInt();
}
* True if the task can go in the docked stack.
* @hide
*/
- public boolean isDockable;
+ public boolean supportsSplitScreenMultiWindow;
/**
* The resize mode of the task. See {@link ActivityInfo#resizeMode}.
Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
dest.writeInt(numActivities);
dest.writeInt(numRunning);
- dest.writeInt(isDockable ? 1 : 0);
+ dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
dest.writeInt(resizeMode);
}
description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
numActivities = source.readInt();
numRunning = source.readInt();
- isDockable = source.readInt() != 0;
+ supportsSplitScreenMultiWindow = source.readInt() != 0;
resizeMode = source.readInt();
}
*/
public static final int RESIZE_MODE_RESIZEABLE = 2;
/**
- * Activity is resizeable and supported picture-in-picture mode.
+ * Activity is resizeable and supported picture-in-picture mode. This flag is now deprecated
+ * since activities do not need to be resizeable to support picture-in-picture.
+ * See {@link #FLAG_SUPPORTS_PICTURE_IN_PICTURE}.
+ *
* @hide
+ * @deprecated
*/
- public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE = 3;
+ public static final int RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED = 3;
/**
* Activity does not support resizing, but we are forcing it to be resizeable. Only affects
* certain pre-N apps where we force them to be resizeable.
public static final int FLAG_VISIBLE_TO_EPHEMERAL = 0x100000;
/**
+ * Bit in {@link #flags} indicating if the activity supports picture-in-picture mode.
+ * See {@link android.R.attr#supportsPictureInPicture}.
+ * @hide
+ */
+ public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x200000;
+
+ /**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the system user. Only works with broadcast receivers. Set from the
* android.R.attr#systemUserOnly attribute.
|| screenOrientation == SCREEN_ORIENTATION_USER_PORTRAIT;
}
+ /**
+ * Returns true if the activity supports picture-in-picture.
+ * @hide
+ */
+ public boolean supportsPictureInPicture() {
+ return (flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0;
+ }
+
/** @hide */
public static boolean isResizeableMode(int mode) {
return mode == RESIZE_MODE_RESIZEABLE
- || mode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE
|| mode == RESIZE_MODE_FORCE_RESIZEABLE
|| mode == RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY
|| mode == RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY
return "RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION";
case RESIZE_MODE_RESIZEABLE:
return "RESIZE_MODE_RESIZEABLE";
- case RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
- return "RESIZE_MODE_RESIZEABLE_AND_PIPABLE";
case RESIZE_MODE_FORCE_RESIZEABLE:
return "RESIZE_MODE_FORCE_RESIZEABLE";
case RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY:
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER;
+import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
// cannot be windowed / resized. Note that an SDK version of 0 is common for
// pre-Doughnut applications.
if (pkg.applicationInfo.usesCompatibilityMode()) {
- adjustPackageToBeUnresizeable(pkg);
+ adjustPackageToBeUnresizeableAndUnpipable(pkg);
}
return pkg;
}
*
* @param pkg The package which needs to be marked as unresizable.
*/
- private void adjustPackageToBeUnresizeable(Package pkg) {
+ private void adjustPackageToBeUnresizeableAndUnpipable(Package pkg) {
for (Activity a : pkg.activities) {
a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+ a.info.flags &= ~FLAG_SUPPORTS_PICTURE_IN_PICTURE;
}
}
setActivityResizeMode(a.info, sa, owner);
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
+ false)) {
+ a.info.flags |= FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+ }
+
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
}
private void setActivityResizeMode(ActivityInfo aInfo, TypedArray sa, Package owner) {
final boolean appExplicitDefault = (owner.applicationInfo.privateFlags
& PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_EXPLICITLY_SET) != 0;
- final boolean supportsPip =
- sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture, false);
if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
|| appExplicitDefault) {
// Activity or app explicitly set if it is resizeable or not;
if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
appExplicitDefault)) {
- aInfo.resizeMode =
- supportsPip ? RESIZE_MODE_RESIZEABLE_AND_PIPABLE : RESIZE_MODE_RESIZEABLE;
+ aInfo.resizeMode = RESIZE_MODE_RESIZEABLE;
} else {
aInfo.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
resizeable activities when in multi-window mode. -->
<attr name="resizeableActivity" format="boolean" />
- <!-- Indicates that the activity supports the picture-in-picture (PiP) form of multi-window.
- While it makes sense to be able to resize most activities types in multi-window mode when
- {@link android.R.attr#resizeableActivity} is set. It only makes sense to put specific types
- of activities in PiP mode of multi-window. For example, activities that play video. When
- set the activity will be allowed to enter PiP mode when the system deems it appropriate on
- devices that support PiP.
+ <!-- Indicates that the activity specifically supports the picture-in-picture form of
+ multi-window. If true, this activity will support entering picture-in-picture, but will
+ only support split-screen and other forms of multi-window if
+ {@link android.R.attr#resizeableActivity} is also set to true.
- <p>The default value is <code>false</code> for applications with
- <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#N} and
- <code>true</code> otherwise.
+ Note that your activity may still be resized even if this attribute is true and
+ {@link android.R.attr#resizeableActivity} is false.
- <p>NOTE: Attribute is only used if {@link android.R.attr#resizeableActivity} is true. -->
+ <p>The default value is <code>false</code>. -->
<attr name="supportsPictureInPicture" format="boolean" />
<!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
ActivityManager.StackId.isHomeOrRecentsStack(runningTask.stackId);
if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
- if (runningTask.isDockable) {
+ if (runningTask.supportsSplitScreenMultiWindow) {
if (metricsDockAction != -1) {
MetricsLogger.action(mContext, metricsDockAction,
runningTask.topActivity.flattenToShortString());
case ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE:
return COUNTER_WINDOW_UNSUPPORTED;
case ActivityInfo.RESIZE_MODE_RESIZEABLE:
- case ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE:
case ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION:
return COUNTER_WINDOW_SUPPORTED;
default:
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
- t.isDockable, t.bounds, t.taskDescription, t.resizeMode, t.topActivity,
+ t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode, t.topActivity,
isLocked);
allTasks.add(task);
private boolean onLongPressRecents() {
if (mRecents == null || !ActivityManager.supportsMultiWindow()
- || !mDivider.getView().getSnapAlgorithm()
- .isSplitScreenFeasible()) {
+ || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()) {
return false;
}
if (tr.mBounds != null) {
rti.bounds = new Rect(tr.mBounds);
}
- rti.isDockable = tr.canGoInDockedStack();
+ rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreen();
rti.resizeMode = tr.mResizeMode;
ActivityRecord base = null;
if (supportsMultiWindow || forceResizable) {
mSupportsMultiWindow = true;
mSupportsFreeformWindowManagement = freeformWindowManagement || forceResizable;
- mSupportsPictureInPicture = supportsPictureInPicture || forceResizable;
} else {
mSupportsMultiWindow = false;
mSupportsFreeformWindowManagement = false;
- mSupportsPictureInPicture = false;
}
mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
+ mSupportsPictureInPicture = supportsPictureInPicture;
mWindowManager.setForceResizableTasks(mForceResizableActivities);
mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
// This happens before any activities are started, so we can change global configuration
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.os.Build.VERSION_CODES.HONEYCOMB;
import android.annotation.NonNull;
import android.app.ActivityManager.TaskDescription;
import android.app.ActivityOptions;
-import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PictureInPictureArgs;
import android.app.ResultInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
}
if (info != null) {
pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
+ pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture());
}
pw.println(prefix + "supportsPictureInPictureWhilePausing: "
+ supportsPictureInPictureWhilePausing);
}
boolean isResizeable() {
- return ActivityInfo.isResizeableMode(info.resizeMode);
+ return ActivityInfo.isResizeableMode(info.resizeMode) || info.supportsPictureInPicture();
}
- boolean isResizeableOrForced() {
- return !isHomeActivity() && (isResizeable() || service.mForceResizableActivities);
- }
-
- boolean isNonResizableOrForced() {
+ /**
+ * @return whether this activity is non-resizeable or forced to be resizeable
+ */
+ boolean isNonResizableOrForcedResizable() {
return info.resizeMode != RESIZE_MODE_RESIZEABLE
- && info.resizeMode != RESIZE_MODE_RESIZEABLE_AND_PIPABLE
&& info.resizeMode != RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
}
/**
- * @return whether this activity's resize mode supports PIP.
+ * @return whether this activity supports PiP multi-window and can be put in the pinned stack.
*/
boolean supportsPictureInPicture() {
- return !isHomeActivity() && info.resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE;
+ return service.mSupportsPictureInPicture && !isHomeActivity()
+ && info.supportsPictureInPicture();
+ }
+
+ /**
+ * @return whether this activity supports split-screen multi-window and can be put in the docked
+ * stack.
+ */
+ boolean supportsSplitScreen() {
+ // An activity can not be docked even if it is considered resizeable because it only
+ // supports picture-in-picture mode but has a non-resizeable resizeMode
+ return service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow();
+ }
+
+ /**
+ * @return whether this activity supports freeform multi-window and can be put in the freeform
+ * stack.
+ */
+ boolean supportsFreeform() {
+ return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
+ }
+
+ /**
+ * @return whether this activity supports non-PiP multi-window.
+ */
+ private boolean supportsResizeableMultiWindow() {
+ return service.mSupportsMultiWindow && !isHomeActivity()
+ && (ActivityInfo.isResizeableMode(info.resizeMode)
+ || service.mForceResizableActivities);
}
/**
return false;
}
- boolean canGoInDockedStack() {
- return !isHomeActivity() && isResizeableOrForced();
- }
-
boolean isAlwaysFocusable() {
return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
}
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
-import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
import static java.lang.Integer.MAX_VALUE;
-import static java.lang.Integer.MIN_VALUE;
import android.app.Activity;
import android.app.ActivityManager;
// home task even though it's not resizable.
final ActivityRecord r = focusedStack.topRunningActivityLocked();
final TaskRecord task = r != null ? r.task : null;
- return task == null || task.canGoInDockedStack() || task.isHomeTask() ? STACK_VISIBLE
+ return task == null || task.supportsSplitScreen() || task.isHomeTask() ? STACK_VISIBLE
: STACK_INVISIBLE;
}
}
ci.numActivities = numActivities;
ci.numRunning = numRunning;
- ci.isDockable = task.canGoInDockedStack();
+ ci.supportsSplitScreenMultiWindow = task.supportsSplitScreen();
ci.resizeMode = task.mResizeMode;
list.add(ci);
}
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static java.lang.Integer.MAX_VALUE;
-import static java.lang.Integer.MIN_VALUE;
import android.Manifest;
import android.annotation.NonNull;
// This means that tasks that were on external displays will be restored on the
// primary display.
stackId = task.getLaunchStackId();
- } else if (stackId == DOCKED_STACK_ID && !task.canGoInDockedStack()) {
+ } else if (stackId == DOCKED_STACK_ID && !task.supportsSplitScreen()) {
// Preferred stack is the docked stack, but the task can't go in the docked stack.
// Put it in the fullscreen stack.
stackId = FULLSCREEN_WORKSPACE_STACK_ID;
}
final ActivityRecord topActivity = task.getTopActivity();
- if (!task.canGoInDockedStack() || forceNonResizable) {
+ if (!task.supportsSplitScreen() || forceNonResizable) {
// Display a warning toast that we tried to put a non-dockable task in the docked stack.
mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
- } else if (topActivity != null && topActivity.isNonResizableOrForced()
+ } else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
&& !topActivity.noDisplay) {
String packageName = topActivity.appInfo.packageName;
mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
canUseFocusedStack = true;
break;
case DOCKED_STACK_ID:
- canUseFocusedStack = r.canGoInDockedStack();
+ canUseFocusedStack = r.supportsSplitScreen();
break;
case FREEFORM_WORKSPACE_STACK_ID:
- canUseFocusedStack = r.isResizeableOrForced();
+ canUseFocusedStack = r.supportsFreeform();
break;
default:
canUseFocusedStack = isDynamicStack(focusedStackId)
}
boolean isValidLaunchStackId(int stackId, ActivityRecord r) {
- if (stackId == INVALID_STACK_ID || stackId == HOME_STACK_ID) {
- return false;
- }
-
- if (stackId != FULLSCREEN_WORKSPACE_STACK_ID
- && (!mService.mSupportsMultiWindow || !r.isResizeableOrForced())) {
- return false;
- }
-
- if (stackId == DOCKED_STACK_ID && r.canGoInDockedStack()) {
- return true;
- }
-
- if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
- return false;
- }
-
- final boolean supportsPip = mService.mSupportsPictureInPicture
- && (r.supportsPictureInPicture() || mService.mForceResizableActivities);
- if (stackId == PINNED_STACK_ID && !supportsPip) {
- return false;
+ switch (stackId) {
+ case INVALID_STACK_ID:
+ case HOME_STACK_ID:
+ return false;
+ case FULLSCREEN_WORKSPACE_STACK_ID:
+ return true;
+ case FREEFORM_WORKSPACE_STACK_ID:
+ return r.supportsFreeform();
+ case DOCKED_STACK_ID:
+ return r.supportsSplitScreen();
+ case PINNED_STACK_ID:
+ return r.supportsPictureInPicture();
+ case RECENTS_STACK_ID:
+ return r.isRecentsActivity();
+ default:
+ Slog.e(TAG, "isValidLaunchStackId: Unexpected stackId=" + stackId);
+ return false;
}
- return true;
}
Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) {
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
private static final String ATTR_CALLING_UID = "calling_uid";
private static final String ATTR_CALLING_PACKAGE = "calling_package";
+ private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
private static final String ATTR_RESIZE_MODE = "resize_mode";
private static final String ATTR_PRIVILEGED = "privileged";
private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
int mResizeMode; // The resize mode of this task and its activities.
// Based on the {@link ActivityInfo#resizeMode} of the root activity.
+ boolean mSupportsPictureInPicture; // Whether or not this task and its activities support PiP.
+ // Based on the {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE} flag of the root
+ // activity.
boolean mTemporarilyUnresizable; // Separate flag from mResizeMode used to suppress resize
// changes on a temporary basis.
private int mLockTaskMode; // Which tasklock mode to launch this task in. One of
boolean neverRelinquishIdentity, TaskDescription _lastTaskDescription,
TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId,
int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
- int resizeMode, boolean privileged, boolean _realActivitySuspended,
- boolean userSetupComplete, int minWidth, int minHeight) {
+ int resizeMode, boolean supportsPictureInPicture, boolean privileged,
+ boolean _realActivitySuspended, boolean userSetupComplete, int minWidth,
+ int minHeight) {
mService = service;
mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX +
TaskPersister.IMAGE_EXTENSION;
mCallingUid = callingUid;
mCallingPackage = callingPackage;
mResizeMode = resizeMode;
+ mSupportsPictureInPicture = supportsPictureInPicture;
mPrivileged = privileged;
mMinWidth = minWidth;
mMinHeight = minHeight;
final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
final Configuration overrideConfig = getOverrideConfiguration();
mWindowContainerController = new TaskWindowContainerController(taskId, this, getStackId(),
- userId, bounds, overrideConfig, mResizeMode, isHomeTask(), isOnTopLauncher(), onTop,
- showForAllUsers, lastTaskDescription);
+ userId, bounds, overrideConfig, mResizeMode, mSupportsPictureInPicture,
+ isHomeTask(), isOnTopLauncher(), onTop, showForAllUsers, lastTaskDescription);
}
void removeWindowContainer() {
autoRemoveRecents = false;
}
mResizeMode = info.resizeMode;
+ mSupportsPictureInPicture = info.supportsPictureInPicture();
mIsOnTopLauncher = (info.flags & FLAG_ON_TOP_LAUNCHER) != 0;
mLockTaskMode = info.lockTaskLaunchMode;
mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
return mTaskToReturnTo == HOME_ACTIVITY_TYPE;
}
+ private boolean isResizeable(boolean checkSupportsPip) {
+ return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
+ || (checkSupportsPip && mSupportsPictureInPicture)) && !mTemporarilyUnresizable;
+ }
+
boolean isResizeable() {
- return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode))
- && !mTemporarilyUnresizable;
+ return isResizeable(true /* checkSupportsPip */);
+ }
+
+ boolean supportsSplitScreen() {
+ // A task can not be docked even if it is considered resizeable because it only supports
+ // picture-in-picture mode but has a non-resizeable resizeMode
+ return mService.mSupportsSplitScreenMultiWindow
+ && isResizeable(false /* checkSupportsPip */)
+ && !ActivityInfo.isPreserveOrientationMode(mResizeMode);
}
/**
return isHomeTask() && mIsOnTopLauncher;
}
- boolean canGoInDockedStack() {
- return isResizeable() && mService.mSupportsSplitScreenMultiWindow &&
- !ActivityInfo.isPreserveOrientationMode(mResizeMode);
- }
-
/**
* Find the activity in the history stack within the given task. Returns
* the index within the history at which it's found, or < 0 if not found.
out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
+ out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
+ String.valueOf(mSupportsPictureInPicture));
out.attribute(null, ATTR_PRIVILEGED, String.valueOf(mPrivileged));
if (mLastNonFullscreenBounds != null) {
out.attribute(
int callingUid = -1;
String callingPackage = "";
int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+ boolean supportsPictureInPicture = false;
boolean privileged = false;
Rect bounds = null;
int minWidth = INVALID_MIN_SIZE;
callingPackage = attrValue;
} else if (ATTR_RESIZE_MODE.equals(attrName)) {
resizeMode = Integer.parseInt(attrValue);
+ } else if (ATTR_SUPPORTS_PICTURE_IN_PICTURE.equals(attrName)) {
+ supportsPictureInPicture = Boolean.parseBoolean(attrValue);
} else if (ATTR_PRIVILEGED.equals(attrName)) {
privileged = Boolean.parseBoolean(attrValue);
} else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
if (taskType == HOME_ACTIVITY_TYPE && resizeMode == RESIZE_MODE_RESIZEABLE) {
resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
}
+ } else {
+ // This activity has previously marked itself explicitly as both resizeable and
+ // supporting picture-in-picture. Since there is no longer a requirement for
+ // picture-in-picture activities to be resizeable, we can mark this simply as
+ // resizeable and supporting picture-in-picture separately.
+ if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
+ resizeMode = RESIZE_MODE_RESIZEABLE;
+ supportsPictureInPicture = true;
+ }
}
final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
autoRemoveRecents, askedCompatMode, taskType, userId, effectiveUid, lastDescription,
activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity,
taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId,
- taskAffiliationColor, callingUid, callingPackage, resizeMode, privileged,
- realActivitySuspended, userSetupComplete, minWidth, minHeight);
+ taskAffiliationColor, callingUid, callingPackage, resizeMode,
+ supportsPictureInPicture, privileged, realActivitySuspended, userSetupComplete,
+ minWidth, minHeight);
task.updateOverrideConfiguration(bounds);
for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
pw.print(prefix); pw.print("stackId="); pw.println(getStackId());
pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
+ pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
pw.print(" isResizeable=" + isResizeable());
pw.print(" firstActiveTime=" + lastActiveTime);
pw.print(" lastActiveTime=" + lastActiveTime);
// Resize mode of the task. See {@link ActivityInfo#resizeMode}
private int mResizeMode;
+ // Whether the task supports picture-in-picture.
+ // See {@link ActivityInfo#FLAG_SUPPORTS_PICTURE_IN_PICTURE}
+ private boolean mSupportsPictureInPicture;
+
// Whether the task is currently being drag-resized
private boolean mDragResizing;
private int mDragResizeMode;
private TaskDescription mTaskDescription;
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
- Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode, boolean homeTask,
- TaskDescription taskDescription, TaskWindowContainerController controller) {
+ Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode,
+ boolean supportsPictureInPicture, boolean homeTask, TaskDescription taskDescription,
+ TaskWindowContainerController controller) {
mTaskId = taskId;
mStack = stack;
mUserId = userId;
mService = service;
mIsOnTopLauncher = isOnTopLauncher;
mResizeMode = resizeMode;
+ mSupportsPictureInPicture = supportsPictureInPicture;
mHomeTask = homeTask;
setController(controller);
setBounds(bounds, overrideConfig);
}
boolean isResizeable() {
- return ActivityInfo.isResizeableMode(mResizeMode) || mService.mForceResizableTasks;
+ return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
+ || mService.mForceResizableTasks;
}
/**
public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
int stackId, int userId, Rect bounds, Configuration overrideConfig, int resizeMode,
- boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers,
- TaskDescription taskDescription) {
+ boolean supportsPictureInPicture, boolean homeTask, boolean isOnTopLauncher,
+ boolean toTop, boolean showForAllUsers, TaskDescription taskDescription) {
super(listener, WindowManagerService.getInstance());
mTaskId = taskId;
}
EventLog.writeEvent(WM_TASK_CREATED, taskId, stackId);
final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
- homeTask, isOnTopLauncher, taskDescription);
+ supportsPictureInPicture, homeTask, isOnTopLauncher, taskDescription);
final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
stack.addTask(task, position, showForAllUsers, true /* moveParents */);
}
@VisibleForTesting
Task createTask(int taskId, TaskStack stack, int userId, Rect bounds,
- Configuration overrideConfig, int resizeMode, boolean homeTask,
- boolean isOnTopLauncher, TaskDescription taskDescription) {
+ Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+ boolean homeTask, boolean isOnTopLauncher, TaskDescription taskDescription) {
return new Task(taskId, stack, userId, mService, bounds, overrideConfig, isOnTopLauncher,
- resizeMode, homeTask, taskDescription, this);
+ resizeMode, supportsPictureInPicture, homeTask, taskDescription, this);
}
@Override
final Rect mInsetBounds = new Rect();
boolean mFullscreenForTest = true;
TaskWithBounds(Rect bounds) {
- super(0, mStubStack, 0, sWm, null, null, false, 0, false, new TaskDescription(), null);
+ super(0, mStubStack, 0, sWm, null, null, false, 0, false, false, new TaskDescription(),
+ null);
mBounds = bounds;
}
@Override
/**Creates a {@link Task} and adds it to the specified {@link TaskStack}. */
static Task createTaskInStack(TaskStack stack, int userId) {
final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, false, 0,
- false, new TaskDescription(), null);
+ false, false, new TaskDescription(), null);
stack.addTask(newTask, POSITION_TOP);
return newTask;
}
TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode,
- boolean homeTask, TaskWindowContainerController controller) {
+ boolean supportsPictureInPicture, boolean homeTask,
+ TaskWindowContainerController controller) {
super(taskId, stack, userId, service, bounds, overrideConfig, isOnTopLauncher,
- resizeMode, homeTask, new TaskDescription(), controller);
+ resizeMode, supportsPictureInPicture, homeTask, new TaskDescription(),
+ controller);
}
boolean shouldDeferRemoval() {
TestTaskWindowContainerController(int stackId) {
super(sNextTaskId++, snapshot -> {}, stackId, 0 /* userId */, null /* bounds */,
- EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, false /* homeTask*/,
+ EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE,
+ false /* supportsPictureInPicture */, false /* homeTask*/,
false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */,
new TaskDescription());
}
@Override
TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds,
- Configuration overrideConfig, int resizeMode, boolean homeTask,
- boolean isOnTopLauncher, TaskDescription taskDescription) {
+ Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
+ boolean homeTask, boolean isOnTopLauncher, TaskDescription taskDescription) {
return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig,
- isOnTopLauncher, resizeMode, homeTask, this);
+ isOnTopLauncher, resizeMode, supportsPictureInPicture, homeTask, this);
}
}