diff options
18 files changed, 182 insertions, 115 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index a9d1cf6e490f..8f169c85d676 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3108,7 +3108,7 @@ public class Activity extends ContextThemeWrapper */ @Override public void enterPictureInPictureModeIfPossible() { - if (mActivityInfo.resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE) { + if (mActivityInfo.supportsPictureInPicture()) { enterPictureInPictureMode(); } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index c1a888d28428..3cb920a6f28d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1482,7 +1482,7 @@ public class ActivityManager { * 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}. @@ -1533,7 +1533,7 @@ public class ActivityManager { } else { dest.writeInt(0); } - dest.writeInt(isDockable ? 1 : 0); + dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0); dest.writeInt(resizeMode); } @@ -1557,7 +1557,7 @@ public class ActivityManager { numActivities = source.readInt(); bounds = source.readInt() > 0 ? Rect.CREATOR.createFromParcel(source) : null; - isDockable = source.readInt() == 1; + supportsSplitScreenMultiWindow = source.readInt() == 1; resizeMode = source.readInt(); } @@ -1745,7 +1745,7 @@ public class ActivityManager { * 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}. @@ -1775,7 +1775,7 @@ public class ActivityManager { Parcelable.PARCELABLE_WRITE_RETURN_VALUE); dest.writeInt(numActivities); dest.writeInt(numRunning); - dest.writeInt(isDockable ? 1 : 0); + dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0); dest.writeInt(resizeMode); } @@ -1792,7 +1792,7 @@ public class ActivityManager { description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); numActivities = source.readInt(); numRunning = source.readInt(); - isDockable = source.readInt() != 0; + supportsSplitScreenMultiWindow = source.readInt() != 0; resizeMode = source.readInt(); } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 4bd091dae77e..4a94688315e8 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -177,10 +177,14 @@ public class ActivityInfo extends ComponentInfo */ 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. @@ -369,6 +373,13 @@ public class ActivityInfo extends ComponentInfo 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. @@ -926,10 +937,17 @@ public class ActivityInfo extends ComponentInfo || 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 @@ -953,8 +971,6 @@ public class ActivityInfo extends ComponentInfo 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: diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ca3011e0c7fb..43ebf46f4996 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -18,12 +18,12 @@ package android.content.pm; 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; @@ -2402,7 +2402,7 @@ public class PackageParser { // 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; } @@ -2413,9 +2413,10 @@ public class PackageParser { * * @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; } } @@ -4000,6 +4001,11 @@ public class PackageParser { 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; } @@ -4161,16 +4167,13 @@ public class PackageParser { 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; } diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index dfa672d6782c..64a64f79b4c3 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1159,18 +1159,15 @@ 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. diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 0265c9e40112..8d18a75e6bdf 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -438,7 +438,7 @@ public class Recents extends SystemUI 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()); @@ -486,7 +486,6 @@ public class Recents extends SystemUI 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: diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 5c7496d437f4..11b598478bfb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -197,7 +197,7 @@ public class RecentsTaskLoadPlan { 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); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 3c46d269750f..d40326a8246e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -546,8 +546,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private boolean onLongPressRecents() { if (mRecents == null || !ActivityManager.supportsMultiWindow() - || !mDivider.getView().getSnapAlgorithm() - .isSplitScreenFeasible()) { + || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()) { return false; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e7e07fe8594b..f6fbaf96644a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9341,7 +9341,7 @@ public class ActivityManagerService extends IActivityManager.Stub 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; @@ -13404,13 +13404,12 @@ public class ActivityManagerService extends IActivityManager.Stub 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 diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 3bd44f5cd3c9..2a849b6d573d 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -39,7 +39,6 @@ import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; 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; @@ -66,7 +65,6 @@ import static com.android.server.am.TaskRecord.INVALID_TASK_ID; 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; @@ -78,7 +76,6 @@ import android.content.res.CompatibilityInfo; 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; @@ -454,6 +451,7 @@ final class ActivityRecord implements AppWindowContainerListener { } if (info != null) { pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode)); + pw.println(prefix + "supportsPictureInPicture=" + info.supportsPictureInPicture()); } pw.println(prefix + "supportsPictureInPictureWhilePausing: " + supportsPictureInPictureWhilePausing); @@ -879,24 +877,50 @@ final class ActivityRecord implements AppWindowContainerListener { } 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); } /** @@ -945,10 +969,6 @@ final class ActivityRecord implements AppWindowContainerListener { return false; } - boolean canGoInDockedStack() { - return !isHomeActivity() && isResizeableOrForced(); - } - boolean isAlwaysFocusable() { return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0; } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 963a9dcb1b01..7bfea9aeb67f 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -62,7 +62,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA 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; @@ -73,7 +72,6 @@ import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN_BEHIND; 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; @@ -1545,7 +1543,7 @@ final class ActivityStack extends ConfigurationContainer { // 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; } @@ -4665,7 +4663,7 @@ final class ActivityStack extends ConfigurationContainer { } ci.numActivities = numActivities; ci.numRunning = numRunning; - ci.isDockable = task.canGoInDockedStack(); + ci.supportsSplitScreenMultiWindow = task.supportsSplitScreen(); ci.resizeMode = task.mResizeMode; list.add(ci); } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 2c1c3a191d11..24ff1d7471f0 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -90,7 +90,6 @@ import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE; 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; @@ -2576,7 +2575,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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; @@ -3895,14 +3894,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } 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( diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 873f2b7a04b6..8d3277893fde 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1967,10 +1967,10 @@ class ActivityStarter { 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) @@ -2057,29 +2057,24 @@ class ActivityStarter { } 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) { diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 7b4d289ac82a..2373ea8975d8 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -85,6 +85,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRA 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; @@ -141,6 +142,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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"; @@ -188,6 +190,9 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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 @@ -356,8 +361,9 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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; @@ -396,6 +402,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta mCallingUid = callingUid; mCallingPackage = callingPackage; mResizeMode = resizeMode; + mSupportsPictureInPicture = supportsPictureInPicture; mPrivileged = privileged; mMinWidth = minWidth; mMinHeight = minHeight; @@ -415,8 +422,8 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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() { @@ -691,6 +698,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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; @@ -1301,9 +1309,21 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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); } /** @@ -1329,11 +1349,6 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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. @@ -1482,6 +1497,8 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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( @@ -1552,6 +1569,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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; @@ -1618,6 +1636,8 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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)) { @@ -1690,6 +1710,15 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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, @@ -1697,8 +1726,9 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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) { @@ -2084,6 +2114,7 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 3a3ec71ff86e..c5ed6eca79d4 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -82,6 +82,10 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU // 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; @@ -94,14 +98,16 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU 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); @@ -327,7 +333,8 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU } boolean isResizeable() { - return ActivityInfo.isResizeableMode(mResizeMode) || mService.mForceResizableTasks; + return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture + || mService.mForceResizableTasks; } /** diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java index 61a2cd96f6c5..fbc3389881b0 100644 --- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java +++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java @@ -63,8 +63,8 @@ public class TaskWindowContainerController 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; @@ -81,7 +81,7 @@ public class TaskWindowContainerController } 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 */); } @@ -89,10 +89,10 @@ public class TaskWindowContainerController @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 diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index 085cfd8b1ef0..186884b01d21 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -77,7 +77,8 @@ public class WindowFrameTests { 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 diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index ae344ddb38f3..847f34dfd729 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -181,7 +181,7 @@ class WindowTestsBase { /**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; } @@ -238,9 +238,11 @@ class WindowTestsBase { 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() { @@ -270,17 +272,18 @@ class WindowTestsBase { 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); } } |