Merge changes I8ac518ce,I8392b4c3,I3bef3f75
* changes:
Support for activity to opt-in/out of PiP form of multi-window.
API for moving top activity in a stack to pinned stack.
Added support for pinned stack.
diff --git a/api/current.txt b/api/current.txt
index f75ef0b..e912440 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -321,7 +321,7 @@
field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
field public static final int buttonBarStyle = 16843566; // 0x101032e
- field public static final int buttonGravity = 16844030; // 0x10104fe
+ field public static final int buttonGravity = 16844031; // 0x10104ff
field public static final int buttonStyle = 16842824; // 0x1010048
field public static final int buttonStyleInset = 16842826; // 0x101004a
field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -371,7 +371,7 @@
field public static final int codes = 16843330; // 0x1010242
field public static final int collapseColumns = 16843083; // 0x101014b
field public static final int collapseContentDescription = 16843984; // 0x10104d0
- field public static final int collapseIcon = 16844031; // 0x10104ff
+ field public static final int collapseIcon = 16844032; // 0x1010500
field public static final int color = 16843173; // 0x10101a5
field public static final int colorAccent = 16843829; // 0x1010435
field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -412,7 +412,7 @@
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contextClickable = 16844007; // 0x10104e7
- field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
+ field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -780,7 +780,7 @@
field public static final int layout_y = 16843136; // 0x1010180
field public static final int left = 16843181; // 0x10101ad
field public static final int letterSpacing = 16843958; // 0x10104b6
- field public static final int level = 16844032; // 0x1010500
+ field public static final int level = 16844033; // 0x1010501
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
field public static final int lines = 16843092; // 0x1010154
@@ -813,7 +813,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
- field public static final int maxButtonHeight = 16844029; // 0x10104fd
+ field public static final int maxButtonHeight = 16844030; // 0x10104fe
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
field public static final int maxHeight = 16843040; // 0x1010120
@@ -1174,6 +1174,7 @@
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
+ field public static final int supportsPictureInPicture = 16844024; // 0x10104f8
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
@@ -1222,7 +1223,7 @@
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
- field public static final int textAppearancePopupMenuHeader = 16844034; // 0x1010502
+ field public static final int textAppearancePopupMenuHeader = 16844035; // 0x1010503
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1291,11 +1292,11 @@
field public static final int tintMode = 16843771; // 0x10103fb
field public static final int title = 16843233; // 0x10101e1
field public static final int titleCondensed = 16843234; // 0x10101e2
- field public static final int titleMargin = 16844024; // 0x10104f8
- field public static final int titleMarginBottom = 16844028; // 0x10104fc
- field public static final int titleMarginEnd = 16844026; // 0x10104fa
- field public static final int titleMarginStart = 16844025; // 0x10104f9
- field public static final int titleMarginTop = 16844027; // 0x10104fb
+ field public static final int titleMargin = 16844025; // 0x10104f9
+ field public static final int titleMarginBottom = 16844029; // 0x10104fd
+ field public static final int titleMarginEnd = 16844027; // 0x10104fb
+ field public static final int titleMarginStart = 16844026; // 0x10104fa
+ field public static final int titleMarginTop = 16844028; // 0x10104fc
field public static final int titleTextAppearance = 16843822; // 0x101042e
field public static final int titleTextColor = 16844003; // 0x10104e3
field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -1395,7 +1396,7 @@
field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
- field public static final int windowBackgroundFallback = 16844035; // 0x1010503
+ field public static final int windowBackgroundFallback = 16844036; // 0x1010504
field public static final int windowClipToOutline = 16843947; // 0x10104ab
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
field public static final int windowContentOverlay = 16842841; // 0x1010059
diff --git a/api/system-current.txt b/api/system-current.txt
index c4c6f1c..b996af3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -413,7 +413,7 @@
field public static final int buttonBarNeutralButtonStyle = 16843914; // 0x101048a
field public static final int buttonBarPositiveButtonStyle = 16843913; // 0x1010489
field public static final int buttonBarStyle = 16843566; // 0x101032e
- field public static final int buttonGravity = 16844030; // 0x10104fe
+ field public static final int buttonGravity = 16844031; // 0x10104ff
field public static final int buttonStyle = 16842824; // 0x1010048
field public static final int buttonStyleInset = 16842826; // 0x101004a
field public static final int buttonStyleSmall = 16842825; // 0x1010049
@@ -463,7 +463,7 @@
field public static final int codes = 16843330; // 0x1010242
field public static final int collapseColumns = 16843083; // 0x101014b
field public static final int collapseContentDescription = 16843984; // 0x10104d0
- field public static final int collapseIcon = 16844031; // 0x10104ff
+ field public static final int collapseIcon = 16844032; // 0x1010500
field public static final int color = 16843173; // 0x10101a5
field public static final int colorAccent = 16843829; // 0x1010435
field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -504,7 +504,7 @@
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
field public static final int contextClickable = 16844007; // 0x10104e7
- field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
+ field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
field public static final int controlX2 = 16843774; // 0x10103fe
field public static final int controlY1 = 16843773; // 0x10103fd
@@ -872,7 +872,7 @@
field public static final int layout_y = 16843136; // 0x1010180
field public static final int left = 16843181; // 0x10101ad
field public static final int letterSpacing = 16843958; // 0x10104b6
- field public static final int level = 16844032; // 0x1010500
+ field public static final int level = 16844033; // 0x1010501
field public static final int lineSpacingExtra = 16843287; // 0x1010217
field public static final int lineSpacingMultiplier = 16843288; // 0x1010218
field public static final int lines = 16843092; // 0x1010154
@@ -905,7 +905,7 @@
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
field public static final int matchOrder = 16843855; // 0x101044f
field public static final int max = 16843062; // 0x1010136
- field public static final int maxButtonHeight = 16844029; // 0x10104fd
+ field public static final int maxButtonHeight = 16844030; // 0x10104fe
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
field public static final int maxHeight = 16843040; // 0x1010120
@@ -1270,6 +1270,7 @@
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
+ field public static final int supportsPictureInPicture = 16844024; // 0x10104f8
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb
field public static final int supportsUploading = 16843419; // 0x101029b
@@ -1318,7 +1319,7 @@
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
- field public static final int textAppearancePopupMenuHeader = 16844034; // 0x1010502
+ field public static final int textAppearancePopupMenuHeader = 16844035; // 0x1010503
field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0
field public static final int textAppearanceSearchResultTitle = 16843425; // 0x10102a1
field public static final int textAppearanceSmall = 16842818; // 0x1010042
@@ -1387,11 +1388,11 @@
field public static final int tintMode = 16843771; // 0x10103fb
field public static final int title = 16843233; // 0x10101e1
field public static final int titleCondensed = 16843234; // 0x10101e2
- field public static final int titleMargin = 16844024; // 0x10104f8
- field public static final int titleMarginBottom = 16844028; // 0x10104fc
- field public static final int titleMarginEnd = 16844026; // 0x10104fa
- field public static final int titleMarginStart = 16844025; // 0x10104f9
- field public static final int titleMarginTop = 16844027; // 0x10104fb
+ field public static final int titleMargin = 16844025; // 0x10104f9
+ field public static final int titleMarginBottom = 16844029; // 0x10104fd
+ field public static final int titleMarginEnd = 16844027; // 0x10104fb
+ field public static final int titleMarginStart = 16844026; // 0x10104fa
+ field public static final int titleMarginTop = 16844028; // 0x10104fc
field public static final int titleTextAppearance = 16843822; // 0x101042e
field public static final int titleTextColor = 16844003; // 0x10104e3
field public static final int titleTextStyle = 16843512; // 0x10102f8
@@ -1491,7 +1492,7 @@
field public static final int windowAllowReturnTransitionOverlap = 16843835; // 0x101043b
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
- field public static final int windowBackgroundFallback = 16844035; // 0x1010503
+ field public static final int windowBackgroundFallback = 16844036; // 0x1010504
field public static final int windowClipToOutline = 16843947; // 0x10104ab
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
field public static final int windowContentOverlay = 16842841; // 0x1010059
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 1e2e33d..12780a8 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -154,7 +154,7 @@
" am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
" am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
" am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]\n" +
- " am stack split <STACK_ID> <v|h> [INTENT]\n" +
+ " am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
" am stack positiontask <TASK_ID> <STACK_ID> <POSITION>\n" +
" am stack list\n" +
" am stack info <STACK_ID>\n" +
@@ -297,11 +297,9 @@
" <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom\n" +
" applying the optional [DELAY_MS] between each step.\n" +
"\n" +
- "am stack split: split <STACK_ID> into 2 stacks <v>ertically or <h>orizontally\n" +
- " starting the new stack with [INTENT] if specified. If [INTENT] isn't\n" +
- " specified and the current stack has more than one task, then the top task\n" +
- " of the current task will be moved to the new stack. Command will also force\n" +
- " all current tasks in both stacks to be resizeable.\n" +
+ "am stack move-top-activity-to-pinned-stack: moves the top activity from\n" +
+ " <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the\n" +
+ " bounds of the pinned stack.\n" +
"\n" +
"am stack positiontask: place <TASK_ID> in <STACK_ID> at <POSITION>" +
"\n" +
@@ -1954,25 +1952,34 @@
private void runStack() throws Exception {
String op = nextArgRequired();
- if (op.equals("start")) {
- runStackStart();
- } else if (op.equals("movetask")) {
- runStackMoveTask();
- } else if (op.equals("resize")) {
- runStackResize();
- } else if (op.equals("positiontask")) {
- runStackPositionTask();
- } else if (op.equals("list")) {
- runStackList();
- } else if (op.equals("info")) {
- runStackInfo();
- } else if (op.equals("split")) {
- runStackSplit();
- } else if (op.equals("size-docked-stack-test")) {
- runStackSizeDockedStackTest();
- } else {
- showError("Error: unknown command '" + op + "'");
- return;
+ switch (op) {
+ case "start":
+ runStackStart();
+ break;
+ case "movetask":
+ runStackMoveTask();
+ break;
+ case "resize":
+ runStackResize();
+ break;
+ case "positiontask":
+ runStackPositionTask();
+ break;
+ case "list":
+ runStackList();
+ break;
+ case "info":
+ runStackInfo();
+ break;
+ case "move-top-activity-to-pinned-stack":
+ runMoveTopActivityToPinnedStack();
+ break;
+ case "size-docked-stack-test":
+ runStackSizeDockedStackTest();
+ break;
+ default:
+ showError("Error: unknown command '" + op + "'");
+ break;
}
}
@@ -2072,61 +2079,21 @@
}
}
- private void runStackSplit() throws Exception {
- final int stackId = Integer.valueOf(nextArgRequired());
- final String splitDirection = nextArgRequired();
- Intent intent = null;
- try {
- intent = makeIntent(UserHandle.USER_CURRENT);
- } catch (IllegalArgumentException e) {
- // no intent supplied.
+ private void runMoveTopActivityToPinnedStack() throws Exception {
+ int stackId = Integer.valueOf(nextArgRequired());
+ final Rect bounds = getBounds();
+ if (bounds == null) {
+ System.err.println("Error: invalid input bounds");
+ return;
}
try {
- final StackInfo currentStackInfo = mAm.getStackInfo(stackId);
- // Calculate bounds for new and current stack.
- final Rect currentStackBounds = new Rect(currentStackInfo.bounds);
- final Rect newStackBounds = new Rect(currentStackInfo.bounds);
- if ("v".equals(splitDirection)) {
- currentStackBounds.right = newStackBounds.left = currentStackInfo.bounds.centerX();
- } else if ("h".equals(splitDirection)) {
- currentStackBounds.bottom = newStackBounds.top = currentStackInfo.bounds.centerY();
- } else {
- showError("Error: unknown split direction '" + splitDirection + "'");
- return;
+ if (!mAm.moveTopActivityToPinnedStack(stackId, bounds)) {
+ showError("Didn't move top activity to pinned stack.");
}
-
- // Create new stack
- IActivityContainer container = mAm.createStackOnDisplay(currentStackInfo.displayId);
- if (container == null) {
- showError("Error: Unable to create new stack...");
- }
-
- final int newStackId = container.getStackId();
-
- if (intent != null) {
- container.startActivity(intent);
- } else if (currentStackInfo.taskIds != null && currentStackInfo.taskIds.length > 1) {
- // Move top task over to new stack
- mAm.moveTaskToStack(currentStackInfo.taskIds[currentStackInfo.taskIds.length - 1],
- newStackId, true);
- }
-
- final StackInfo newStackInfo = mAm.getStackInfo(newStackId);
-
- // Make all tasks in the stacks resizeable.
- for (int taskId : currentStackInfo.taskIds) {
- mAm.setTaskResizeable(taskId, true);
- }
-
- for (int taskId : newStackInfo.taskIds) {
- mAm.setTaskResizeable(taskId, true);
- }
-
- // Resize stacks
- mAm.resizeStack(currentStackInfo.stackId, currentStackBounds, false);
- mAm.resizeStack(newStackInfo.stackId, newStackBounds, false);
} catch (RemoteException e) {
+ showError("Unable to move top activity: " + e);
+ return;
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3bfeff0..6ae32d0 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -434,10 +434,17 @@
public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
/**
+ * ID of stack that always on top (always visible) when it exist.
+ * Mainly used for this in Picture-in-Picture mode.
+ * @hide
+ */
+ public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
+
+ /**
* Last static stack stack ID.
* @hide
*/
- public static final int LAST_STATIC_STACK_ID = DOCKED_STACK_ID;
+ public static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
/**
* Start of ID range used by stacks that are created dynamically.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b97f947..70f1bcc 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -755,6 +755,16 @@
return true;
}
+ case MOVE_TOP_ACTIVITY_TO_PINNED_STACK: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int stackId = data.readInt();
+ final Rect r = Rect.CREATOR.createFromParcel(data);
+ final boolean res = moveTopActivityToPinnedStack(stackId, r);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
case RESIZE_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
final int stackId = data.readInt();
@@ -3555,6 +3565,22 @@
reply.recycle();
}
@Override
+ public boolean moveTopActivityToPinnedStack(int stackId, Rect r)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(stackId);
+ r.writeToParcel(data, 0);
+ mRemote.transact(MOVE_TOP_ACTIVITY_TO_PINNED_STACK, data, reply, 0);
+ reply.readException();
+ final boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+ @Override
public void resizeStack(int stackId, Rect r, boolean allowResizeInDockedMode)
throws RemoteException
{
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c26a44c..ebdd302 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -143,6 +143,7 @@
public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
throws RemoteException;
+ public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) throws RemoteException;
public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) throws RemoteException;
public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
public List<StackInfo> getAllStackInfos() throws RemoteException;
@@ -898,4 +899,5 @@
int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348;
+ int MOVE_TOP_ACTIVITY_TO_PINNED_STACK = IBinder.FIRST_CALL_TRANSACTION + 349;
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 3853772..7ca39cb 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -671,6 +671,13 @@
*/
public boolean resizeable;
+ /**
+ * Value indicating if the activity is supports picture-in-picture form of multi-window mode.
+ * See {@link android.R.attr#supportsPictureInPicture}.
+ * @hide
+ */
+ public boolean supportsPip;
+
/** @hide */
public static final int LOCK_TASK_LAUNCH_MODE_DEFAULT = 0;
/** @hide */
@@ -723,6 +730,7 @@
parentActivityName = orig.parentActivityName;
maxRecents = orig.maxRecents;
resizeable = orig.resizeable;
+ supportsPip = orig.supportsPip;
lockTaskLaunchMode = orig.lockTaskLaunchMode;
layout = orig.layout;
}
@@ -769,8 +777,8 @@
if (uiOptions != 0) {
pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
}
- pw.println(prefix + "resizeable=" + resizeable + " lockTaskLaunchMode="
- + lockTaskLaunchModeToString(lockTaskLaunchMode));
+ pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
+ pw.println(prefix + "lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode));
if (layout != null) {
pw.println(prefix + "initialLayout=" + layout.width + "|"
+ layout.widthFraction + ", " + layout.height + "|"
@@ -806,6 +814,7 @@
dest.writeInt(persistableMode);
dest.writeInt(maxRecents);
dest.writeInt(resizeable ? 1 : 0);
+ dest.writeInt(supportsPip ? 1 : 0);
dest.writeInt(lockTaskLaunchMode);
if (layout != null) {
dest.writeInt(1);
@@ -847,6 +856,7 @@
persistableMode = source.readInt();
maxRecents = source.readInt();
resizeable = (source.readInt() == 1);
+ supportsPip = (source.readInt() == 1);
lockTaskLaunchMode = source.readInt();
if (source.readInt() == 1) {
layout = new Layout(source);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1e85dfb..5d73b06 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3220,6 +3220,9 @@
R.styleable.AndroidManifestActivity_resizeableActivity,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N);
+ a.info.supportsPip = a.info.resizeable ? sa.getBoolean(
+ R.styleable.AndroidManifestActivity_supportsPictureInPicture, false) : false;
+
a.info.screenOrientation = sa.getInt(
R.styleable.AndroidManifestActivity_screenOrientation,
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 4644211..afef763 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
@@ -5408,7 +5409,7 @@
* @Return Returns true if the window should show a non client decor.
**/
private static boolean hasNonClientDecor(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
+ return workspaceId == FREEFORM_WORKSPACE_STACK_ID || workspaceId == PINNED_STACK_ID;
}
/**
@@ -5417,7 +5418,7 @@
* @Return Returns true if the window should show a shadow.
**/
private static boolean nonClientDecorHasShadow(int workspaceId) {
- return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
+ return workspaceId == FREEFORM_WORKSPACE_STACK_ID || workspaceId == PINNED_STACK_ID;
}
@Override
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 322ac4f..07ac471 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1059,7 +1059,7 @@
at the same time.
<p>The default value is <code>false</code> for applications with
- <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#M} and
+ <code>targetSdkVersion</code> lesser than {@link android.os.Build.VERSION_CODES#N} and
<code>true</code> otherwise.
<p>NOTE: A task's root activity value is applied to all additional activities launched in
@@ -1067,11 +1067,24 @@
all other activities in the task as resizeable and will not if the root activity isn't
resizeable.
- <p>NOTE: The value of {@link android.R.attr#screenOrientation} will be ignored for
- resizeable activities as the system doesn't support fixed orientation on a resizeable
- activity. -->
+ <p>NOTE: The value of {@link android.R.attr#screenOrientation} is ignored for
+ 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.
+
+ <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.
+
+ <p>NOTE: Attribute is only used if {@link android.R.attr#resizeableActivity} is true. -->
+ <attr name="supportsPictureInPicture" format="boolean" />
+
<!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
While in lockTask mode the system will not launch non-permitted tasks until
lockTask mode is disabled.
@@ -1826,6 +1839,7 @@
<attr name="relinquishTaskIdentity" />
<attr name="resumeWhilePausing" />
<attr name="resizeableActivity" />
+ <attr name="supportsPictureInPicture" />
<attr name="lockTaskMode" />
<attr name="showForAllUsers" />
</declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c05b585..037f1c4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2666,6 +2666,7 @@
<public type="attr" name="initialHeight" />
<public type="attr" name="minimalSize" />
<public type="attr" name="resizeableActivity" />
+ <public type="attr" name="supportsPictureInPicture" />
<public type="attr" name="titleMargin" />
<public type="attr" name="titleMarginStart" />
<public type="attr" name="titleMarginEnd" />
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c728b39..9834757 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24,6 +24,7 @@
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
@@ -8638,9 +8639,10 @@
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- // Note it's not allowed to resize a home stack task, or a docked task.
+ // Note it's not allowed to resize a home, docked, or pinned stack task.
int stackId = task.stack.mStackId;
- if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID) {
+ if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID
+ || stackId == PINNED_STACK_ID) {
throw new IllegalArgumentException("trying to resizeTask on a "
+ "home or docked task");
}
@@ -9081,6 +9083,29 @@
}
}
+ /**
+ * Moves the top activity in the input stackId to the pinned stack.
+ *
+ * @param stackId Id of stack to move the top activity to pinned stack.
+ * @param bounds Bounds to use for pinned stack.
+ *
+ * @return True if the top activity of the input stack was successfully moved to the pinned
+ * stack.
+ */
+ @Override
+ public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "moveTopActivityToPinnedStack()");
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
@Override
public void resizeStack(int stackId, Rect bounds, boolean allowResizeInDockedMode) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 8bf1d22..99539e4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -253,6 +254,9 @@
private final LaunchingTaskPositioner mTaskPositioner;
+ // If the bounds of task contained in this stack should be persisted across power cycles.
+ final boolean mPersistTaskBounds;
+
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -354,10 +358,6 @@
return count;
}
- int numTasks() {
- return mTaskHistory.size();
- }
-
ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
RecentTasks recentTasks) {
mActivityContainer = activityContainer;
@@ -370,6 +370,7 @@
mRecentTasks = recentTasks;
mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
? new LaunchingTaskPositioner() : null;
+ mPersistTaskBounds = mStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID;
}
void attachDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) {
@@ -1306,6 +1307,11 @@
return true;
}
+ if (mStackId == PINNED_STACK_ID) {
+ // Pinned stack is always visible if it exist.
+ return true;
+ }
+
final int stackIndex = mStacks.indexOf(this);
if (stackIndex == mStacks.size() - 1) {
@@ -1328,8 +1334,9 @@
}
final int belowFocusedIndex = mStacks.indexOf(focusedStack) - 1;
- if (focusedStackId == DOCKED_STACK_ID && stackIndex == belowFocusedIndex) {
- // Stacks directly behind the docked stack are always visible.
+ if ((focusedStackId == DOCKED_STACK_ID || focusedStackId == PINNED_STACK_ID)
+ && stackIndex == belowFocusedIndex) {
+ // Stacks directly behind the docked or pinned stack are always visible.
return true;
}
@@ -1343,9 +1350,10 @@
}
if (belowFocusedIndex >= 0) {
final ActivityStack stack = mStacks.get(belowFocusedIndex);
- if (stack.mStackId == DOCKED_STACK_ID && stackIndex == (belowFocusedIndex - 1)) {
- // The stack behind the docked stack is also visible so we can have a complete
- // backdrop to the translucent activity when the docked stack is up.
+ if ((stack.mStackId == DOCKED_STACK_ID || stack.mStackId == PINNED_STACK_ID)
+ && stackIndex == (belowFocusedIndex - 1)) {
+ // The stack behind the docked or pinned stack is also visible so we can have a
+ // complete backdrop to the translucent activity when the docked stack is up.
return true;
}
}
@@ -2784,9 +2792,10 @@
final String myReason = reason + " adjustFocus";
if (next != r) {
if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStackId == DOCKED_STACK_ID)) {
- // For freeform and docked stacks we always keep the focus within the stack as
- // long as there is a running activity in the stack that we can adjust focus to.
+ || mStackId == DOCKED_STACK_ID || mStackId == PINNED_STACK_ID)) {
+ // For freeform, docked, and pinned stacks we always keep the focus within the
+ // stack as long as there is a running activity in the stack that we can adjust
+ // focus to.
mService.setFocusedActivityLocked(next, myReason);
return;
} else {
@@ -4633,6 +4642,51 @@
r.taskConfigOverride = task.mOverrideConfig;
}
+ void setFocusAndResumeStateIfNeeded(
+ ActivityRecord r, boolean setFocus, boolean setResume, String reason) {
+ // If the activity had focus before move focus to this stack.
+ if (setFocus) {
+ // If the activity owns the last resumed activity, transfer that together,
+ // so that we don't resume the same activity again in the new stack.
+ // Apps may depend on onResume()/onPause() being called in pairs.
+ if (setResume) {
+ mResumedActivity = r;
+ // Move the stack in which we are placing the activity to the front. We don't use
+ // ActivityManagerService.setFocusedActivityLocked, because if the activity is
+ // already focused, the call will short-circuit and do nothing.
+ moveToFront(reason);
+ } else {
+ // We need to not only move the stack to the front, but also have the activity
+ // focused. This will achieve both goals.
+ mService.setFocusedActivityLocked(r, reason);
+ }
+ }
+ }
+
+ /**
+ * Moves the input activity from its current stack to this one.
+ * NOTE: The current task of the activity isn't moved to this stack. Instead a new task is
+ * created on this stack which the activity is added to.
+ * */
+ void moveActivityToStack(ActivityRecord r) {
+ final ActivityStack prevStack = r.task.stack;
+ if (prevStack.mStackId == mStackId) {
+ // You are already in the right stack silly...
+ return;
+ }
+
+ final boolean wasFocused = mStackSupervisor.isFrontStack(prevStack)
+ && (mStackSupervisor.topRunningActivityLocked() == r);
+ final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
+
+ final TaskRecord task = createTaskRecord(
+ mStackSupervisor.getNextTaskId(), r.info, r.intent, null, null, true);
+ r.setTask(task, null);
+ task.addActivityToTop(r);
+ setAppTask(r, task);
+ setFocusAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+ }
+
private void setAppTask(ActivityRecord r, TaskRecord task) {
final Rect bounds = task.getLaunchBounds();
task.updateOverrideConfiguration(bounds);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8dec77c..98cfc7c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3256,23 +3256,8 @@
// If the task had focus before (or we're requested to move focus),
// move focus to the new stack.
- if (forceFocus || wasFocused) {
- // If the task owns the last resumed activity, transfer that together,
- // so that we don't resume the same activity again in the new stack.
- // Apps may depend on onResume()/onPause() being called in pairs.
- if (wasResumed) {
- stack.mResumedActivity = r;
- // Move the stack in which we are placing the task to the front. We don't use
- // ActivityManagerService.setFocusedActivityLocked, because if the activity is
- // already focused, the call will short-circuit and do nothing.
- stack.moveToFront(reason);
- } else {
- // We need to not only move the stack to the front, but also have the activity
- // focused. This will achieve both goals.
- mService.setFocusedActivityLocked(r, reason);
- }
-
- }
+ stack.setFocusAndResumeStateIfNeeded(
+ r, forceFocus || wasFocused, wasResumed, reason);
return stack;
}
@@ -3284,7 +3269,8 @@
return;
}
final String reason = "moveTaskToStack";
- if (stackId == DOCKED_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
// and then add a new one. This call will tell window manager about this, so it can
@@ -3305,7 +3291,7 @@
&& task.mBounds == null && task.mLastNonFullscreenBounds != null) {
resizeTaskLocked(task, task.mLastNonFullscreenBounds,
RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
- } else if (stackId == DOCKED_STACK_ID) {
+ } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
resizeTaskLocked(task, stack.mBounds,
RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
}
@@ -3316,6 +3302,46 @@
resumeTopActivitiesLocked();
}
+ boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
+ final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP);
+ if (stack == null) {
+ throw new IllegalArgumentException(
+ "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId);
+ }
+
+ final ActivityRecord r = stack.topRunningActivityLocked();
+ if (r == null) {
+ Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity"
+ + " in stack=" + stack);
+ return false;
+ }
+
+ if (!r.info.supportsPip) {
+ Slog.w(TAG,
+ "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for "
+ + " r=" + r);
+ return false;
+ }
+
+ final TaskRecord task = r.task;
+ if (task.mActivities.size() == 1) {
+ // There is only one activity in the task. So, we can just move the task over to the
+ // pinned stack without re-parenting the activity in a different task.
+ moveTaskToStackLocked(task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS);
+ } else {
+ final ActivityStack pinnedStack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+ pinnedStack.moveActivityToStack(r);
+ }
+
+ resizeStackLocked(PINNED_STACK_ID, bounds, PRESERVE_WINDOWS, true);
+
+ // The task might have already been running and its visibility needs to be synchronized with
+ // the visibility of the stack / windows.
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ resumeTopActivitiesLocked();
+ return true;
+ }
+
void positionTaskInStackLocked(int taskId, int stackId, int position) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index fe87a93..090a342 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_ALWAYS;
@@ -1189,14 +1190,14 @@
mFullscreen = bounds == null;
if (mFullscreen) {
- if (mBounds != null && stack.mStackId != DOCKED_STACK_ID) {
+ if (mBounds != null && stack.mPersistTaskBounds) {
mLastNonFullscreenBounds = mBounds;
}
mBounds = null;
mOverrideConfig = Configuration.EMPTY;
} else {
mBounds = new Rect(bounds);
- if (stack == null || stack.mStackId != DOCKED_STACK_ID) {
+ if (stack == null || stack.mPersistTaskBounds) {
mLastNonFullscreenBounds = mBounds;
}
@@ -1235,11 +1236,12 @@
/** Returns the bounds that should be used to launch this task. */
Rect getLaunchBounds() {
+ final int stackId = stack.mStackId;
if (stack == null
- || stack.mStackId == HOME_STACK_ID
- || stack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ || stackId == HOME_STACK_ID
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
return (mResizeable && stack != null) ? stack.mBounds : null;
- } else if (stack.mStackId == DOCKED_STACK_ID) {
+ } else if (!stack.mPersistTaskBounds) {
return stack.mBounds;
}
return mLastNonFullscreenBounds;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7c56180..6527881 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,12 +17,14 @@
package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_STACK;
import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -187,7 +189,8 @@
bounds = mTmpRect;
mFullscreen = true;
} else {
- if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID || bounds.isEmpty()) {
+ if ((mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+ && mStack.mStackId != PINNED_STACK_ID) || bounds.isEmpty()) {
// ensure bounds are entirely within the display rect
if (!bounds.intersect(mTmpRect)) {
// Can't set bounds outside the containing display...Sorry!
@@ -241,8 +244,7 @@
private boolean useCurrentBounds() {
final DisplayContent displayContent = mStack.getDisplayContent();
if (mFullscreen
- || mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStack.mStackId == DOCKED_STACK_ID
+ || mStack.allowTaskResize()
|| displayContent == null
|| displayContent.getDockedStackLocked() != null) {
return true;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6734fd6..9b3d478 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.LAST_STATIC_STACK_ID;
+import static android.app.ActivityManager.PINNED_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
import static com.android.server.wm.WindowManagerService.TAG;
@@ -123,7 +124,8 @@
boolean allowTaskResize() {
return mStackId == FREEFORM_WORKSPACE_STACK_ID
- || mStackId == DOCKED_STACK_ID;
+ || mStackId == DOCKED_STACK_ID
+ || mStackId == PINNED_STACK_ID;
}
/**
@@ -202,6 +204,7 @@
private boolean useCurrentBounds() {
if (mFullscreen
|| mStackId == DOCKED_STACK_ID
+ || mStackId == PINNED_STACK_ID
|| mDisplayContent == null
|| mDisplayContent.getDockedStackLocked() != null) {
return true;
@@ -393,7 +396,7 @@
Rect bounds = null;
final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
- if (mStackId == DOCKED_STACK_ID || (dockedStack != null
+ if (mStackId == DOCKED_STACK_ID || (dockedStack != null && mStackId != PINNED_STACK_ID
&& mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
// The existence of a docked stack affects the size of any static stack created since
// the docked stack occupies a dedicated region on screen.
@@ -422,6 +425,7 @@
void getStackDockedModeBoundsLocked(Rect outBounds) {
if (mStackId == DOCKED_STACK_ID
+ || mStackId == PINNED_STACK_ID
|| mStackId > LAST_STATIC_STACK_ID
|| mDisplayContent == null) {
outBounds.set(mBounds);
@@ -533,7 +537,7 @@
for (int i = 0; i < count; i++) {
final TaskStack otherStack = mService.mStackIdToStack.valueAt(i);
final int otherStackId = otherStack.mStackId;
- if (otherStackId != DOCKED_STACK_ID
+ if (otherStackId != DOCKED_STACK_ID && mStackId != PINNED_STACK_ID
&& otherStackId >= FIRST_STATIC_STACK_ID
&& otherStackId <= LAST_STATIC_STACK_ID) {
mService.mH.sendMessage(