Merge "Passing stack id with RunningTaskInfo."
diff --git a/api/current.txt b/api/current.txt
index f75ef0b..15cc347 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
@@ -4829,17 +4830,17 @@
field public android.app.Notification.Action[] actions;
field public android.media.AudioAttributes audioAttributes;
field public deprecated int audioStreamType;
- field public android.widget.RemoteViews bigContentView;
+ field public deprecated android.widget.RemoteViews bigContentView;
field public java.lang.String category;
field public int color;
field public android.app.PendingIntent contentIntent;
- field public android.widget.RemoteViews contentView;
+ field public deprecated android.widget.RemoteViews contentView;
field public int defaults;
field public android.app.PendingIntent deleteIntent;
field public android.os.Bundle extras;
field public int flags;
field public android.app.PendingIntent fullScreenIntent;
- field public android.widget.RemoteViews headsUpContentView;
+ field public deprecated android.widget.RemoteViews headsUpContentView;
field public deprecated int icon;
field public int iconLevel;
field public deprecated android.graphics.Bitmap largeIcon;
@@ -4930,14 +4931,22 @@
method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
+ method public android.widget.RemoteViews makeBigContentView();
+ method public android.widget.RemoteViews makeContentView();
+ method public android.widget.RemoteViews makeHeadsUpContentView();
+ method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
+ method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setCategory(java.lang.String);
method public android.app.Notification.Builder setColor(int);
- method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
+ method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setContentText(java.lang.CharSequence);
method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
+ method public android.app.Notification.Builder setCustomBigContentView(android.widget.RemoteViews);
+ method public android.app.Notification.Builder setCustomContentView(android.widget.RemoteViews);
+ method public android.app.Notification.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
method public android.app.Notification.Builder setDefaults(int);
method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setExtras(android.os.Bundle);
@@ -22152,6 +22161,7 @@
field public static final int GL_ACTIVE_PROGRAM = 33369; // 0x8259
field public static final int GL_ACTIVE_RESOURCES = 37621; // 0x92f5
field public static final int GL_ACTIVE_VARIABLES = 37637; // 0x9305
+ field public static final int GL_ALL_BARRIER_BITS = -1; // 0xffffffff
field public static final int GL_ALL_SHADER_BITS = -1; // 0xffffffff
field public static final int GL_ARRAY_SIZE = 37627; // 0x92fb
field public static final int GL_ARRAY_STRIDE = 37630; // 0x92fe
@@ -22175,6 +22185,7 @@
field public static final int GL_DISPATCH_INDIRECT_BUFFER_BINDING = 37103; // 0x90ef
field public static final int GL_DRAW_INDIRECT_BUFFER = 36671; // 0x8f3f
field public static final int GL_DRAW_INDIRECT_BUFFER_BINDING = 36675; // 0x8f43
+ field public static final int GL_ELEMENT_ARRAY_BARRIER_BIT = 2; // 0x2
field public static final int GL_FRAGMENT_SHADER_BIT = 2; // 0x2
field public static final int GL_FRAMEBUFFER_BARRIER_BIT = 1024; // 0x400
field public static final int GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = 37652; // 0x9314
@@ -22264,6 +22275,7 @@
field public static final int GL_SAMPLE_MASK = 36433; // 0x8e51
field public static final int GL_SAMPLE_MASK_VALUE = 36434; // 0x8e52
field public static final int GL_SAMPLE_POSITION = 36432; // 0x8e50
+ field public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 32; // 0x20
field public static final int GL_SHADER_STORAGE_BARRIER_BIT = 8192; // 0x2000
field public static final int GL_SHADER_STORAGE_BLOCK = 37606; // 0x92e6
field public static final int GL_SHADER_STORAGE_BUFFER = 37074; // 0x90d2
@@ -22309,6 +22321,7 @@
field public static final int GL_UNSIGNED_INT_IMAGE_3D = 36964; // 0x9064
field public static final int GL_UNSIGNED_INT_IMAGE_CUBE = 36966; // 0x9066
field public static final int GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = 37130; // 0x910a
+ field public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 1; // 0x1
field public static final int GL_VERTEX_ATTRIB_BINDING = 33492; // 0x82d4
field public static final int GL_VERTEX_ATTRIB_RELATIVE_OFFSET = 33493; // 0x82d5
field public static final int GL_VERTEX_BINDING_BUFFER = 36687; // 0x8f4f
diff --git a/api/system-current.txt b/api/system-current.txt
index c4c6f1c..a9a8753 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
@@ -4946,17 +4947,17 @@
field public android.app.Notification.Action[] actions;
field public android.media.AudioAttributes audioAttributes;
field public deprecated int audioStreamType;
- field public android.widget.RemoteViews bigContentView;
+ field public deprecated android.widget.RemoteViews bigContentView;
field public java.lang.String category;
field public int color;
field public android.app.PendingIntent contentIntent;
- field public android.widget.RemoteViews contentView;
+ field public deprecated android.widget.RemoteViews contentView;
field public int defaults;
field public android.app.PendingIntent deleteIntent;
field public android.os.Bundle extras;
field public int flags;
field public android.app.PendingIntent fullScreenIntent;
- field public android.widget.RemoteViews headsUpContentView;
+ field public deprecated android.widget.RemoteViews headsUpContentView;
field public deprecated int icon;
field public int iconLevel;
field public deprecated android.graphics.Bitmap largeIcon;
@@ -5047,14 +5048,22 @@
method public android.app.Notification.Builder extend(android.app.Notification.Extender);
method public android.os.Bundle getExtras();
method public deprecated android.app.Notification getNotification();
+ method public android.widget.RemoteViews makeBigContentView();
+ method public android.widget.RemoteViews makeContentView();
+ method public android.widget.RemoteViews makeHeadsUpContentView();
+ method public static android.app.Notification.Builder recoverBuilder(android.content.Context, android.app.Notification);
+ method public android.app.Notification.Builder setActions(android.app.Notification.Action...);
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setCategory(java.lang.String);
method public android.app.Notification.Builder setColor(int);
- method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
+ method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setContentText(java.lang.CharSequence);
method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
+ method public android.app.Notification.Builder setCustomBigContentView(android.widget.RemoteViews);
+ method public android.app.Notification.Builder setCustomContentView(android.widget.RemoteViews);
+ method public android.app.Notification.Builder setCustomHeadsUpContentView(android.widget.RemoteViews);
method public android.app.Notification.Builder setDefaults(int);
method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setExtras(android.os.Bundle);
@@ -24097,6 +24106,7 @@
field public static final int GL_ACTIVE_PROGRAM = 33369; // 0x8259
field public static final int GL_ACTIVE_RESOURCES = 37621; // 0x92f5
field public static final int GL_ACTIVE_VARIABLES = 37637; // 0x9305
+ field public static final int GL_ALL_BARRIER_BITS = -1; // 0xffffffff
field public static final int GL_ALL_SHADER_BITS = -1; // 0xffffffff
field public static final int GL_ARRAY_SIZE = 37627; // 0x92fb
field public static final int GL_ARRAY_STRIDE = 37630; // 0x92fe
@@ -24120,6 +24130,7 @@
field public static final int GL_DISPATCH_INDIRECT_BUFFER_BINDING = 37103; // 0x90ef
field public static final int GL_DRAW_INDIRECT_BUFFER = 36671; // 0x8f3f
field public static final int GL_DRAW_INDIRECT_BUFFER_BINDING = 36675; // 0x8f43
+ field public static final int GL_ELEMENT_ARRAY_BARRIER_BIT = 2; // 0x2
field public static final int GL_FRAGMENT_SHADER_BIT = 2; // 0x2
field public static final int GL_FRAMEBUFFER_BARRIER_BIT = 1024; // 0x400
field public static final int GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS = 37652; // 0x9314
@@ -24209,6 +24220,7 @@
field public static final int GL_SAMPLE_MASK = 36433; // 0x8e51
field public static final int GL_SAMPLE_MASK_VALUE = 36434; // 0x8e52
field public static final int GL_SAMPLE_POSITION = 36432; // 0x8e50
+ field public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 32; // 0x20
field public static final int GL_SHADER_STORAGE_BARRIER_BIT = 8192; // 0x2000
field public static final int GL_SHADER_STORAGE_BLOCK = 37606; // 0x92e6
field public static final int GL_SHADER_STORAGE_BUFFER = 37074; // 0x90d2
@@ -24254,6 +24266,7 @@
field public static final int GL_UNSIGNED_INT_IMAGE_3D = 36964; // 0x9064
field public static final int GL_UNSIGNED_INT_IMAGE_CUBE = 36966; // 0x9066
field public static final int GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = 37130; // 0x910a
+ field public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 1; // 0x1
field public static final int GL_VERTEX_ATTRIB_BINDING = 33492; // 0x82d4
field public static final int GL_VERTEX_ATTRIB_RELATIVE_OFFSET = 33493; // 0x82d5
field public static final int GL_VERTEX_BINDING_BUFFER = 36687; // 0x8f4f
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/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 3f8e311..2960cdc 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -283,7 +283,7 @@
} else if (args.length == 2) {
if (args[0].equalsIgnoreCase("-p")) {
validCommand = true;
- return displayPackageFilePath(args[1], UserHandle.USER_OWNER);
+ return displayPackageFilePath(args[1], UserHandle.USER_SYSTEM);
}
}
return 1;
@@ -767,7 +767,7 @@
}
private int runPath() {
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
if (option != null && option.equals("--user")) {
String optionData = nextOptionData();
@@ -1650,7 +1650,7 @@
}
private int runClear() {
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
if (option != null && option.equals("--user")) {
String optionData = nextOptionData();
@@ -1722,7 +1722,7 @@
}
private int runSetEnabledSetting(int state) {
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
if (option != null && option.equals("--user")) {
String optionData = nextOptionData();
@@ -1771,7 +1771,7 @@
}
private int runSetHiddenSetting(boolean state) {
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
String option = nextOption();
if (option != null && option.equals("--user")) {
String optionData = nextOptionData();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 685e2c3..fce1b2e 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/app/Notification.java b/core/java/android/app/Notification.java
index db18722..49edff4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -251,15 +251,26 @@
public RemoteViews tickerView;
/**
- * The view that will represent this notification in the expanded status bar.
+ * The view that will represent this notification in the notification list (which is pulled
+ * down from the status bar).
+ *
+ * As of N, this field is not used. The notification view is determined by the inputs to
+ * {@link Notification.Builder}; a custom RemoteViews can optionally be
+ * supplied with {@link Notification.Builder#setCustomContentView(RemoteViews)}.
*/
+ @Deprecated
public RemoteViews contentView;
/**
* A large-format version of {@link #contentView}, giving the Notification an
* opportunity to show more detail. The system UI may choose to show this
* instead of the normal content view at its discretion.
+ *
+ * As of N, this field is not used. The expanded notification view is determined by the
+ * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
+ * supplied with {@link Notification.Builder#setCustomBigContentView(RemoteViews)}.
*/
+ @Deprecated
public RemoteViews bigContentView;
@@ -268,7 +279,12 @@
* opportunity to add action buttons to contentView. At its discretion, the system UI may
* choose to show this as a heads-up notification, which will pop up so the user can see
* it without leaving their current activity.
+ *
+ * As of N, this field is not used. The heads-up notification view is determined by the
+ * inputs to {@link Notification.Builder}; a custom RemoteViews can optionally be
+ * supplied with {@link Notification.Builder#setCustomHeadsUpContentView(RemoteViews)}.
*/
+ @Deprecated
public RemoteViews headsUpContentView;
/**
@@ -867,6 +883,11 @@
public static final String EXTRA_ORIGINATING_USERID = "android.originatingUserId";
/**
+ * @hide
+ */
+ public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo";
+
+ /**
* Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
* displayed in the heads up space.
*
@@ -1707,8 +1728,6 @@
extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
extras.remove(Notification.EXTRA_PICTURE);
extras.remove(Notification.EXTRA_BIG_TEXT);
- // Prevent light notifications from being rebuilt.
- extras.remove(Builder.EXTRA_NEEDS_REBUILD);
}
}
@@ -1901,21 +1920,13 @@
@Deprecated
public void setLatestEventInfo(Context context,
CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
- Notification.Builder builder = new Notification.Builder(context);
+ if (context.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1){
+ Log.e(TAG, "setLatestEventInfo() is deprecated and you should feel deprecated.",
+ new Throwable());
+ }
- // First, ensure that key pieces of information that may have been set directly
- // are preserved
- builder.setWhen(this.when);
- builder.setSmallIcon(this.icon);
- builder.setPriority(this.priority);
- builder.setTicker(this.tickerText);
- builder.setNumber(this.number);
- builder.setColor(this.color);
- builder.mFlags = this.flags;
- builder.setSound(this.sound, this.audioStreamType);
- builder.setDefaults(this.defaults);
- builder.setVibrate(this.vibrate);
- builder.setDeleteIntent(this.deleteIntent);
+ // ensure that any information already set directly is preserved
+ final Notification.Builder builder = new Notification.Builder(context, this);
// now apply the latestEventInfo fields
if (contentTitle != null) {
@@ -1925,7 +1936,8 @@
builder.setContentText(contentText);
}
builder.setContentIntent(contentIntent);
- builder.buildInto(this);
+
+ builder.build(); // callers expect this notification to be ready to use
}
@Override
@@ -2080,15 +2092,6 @@
/**
* @hide
*/
- public boolean isValid() {
- // Would like to check for icon!=0 here, too, but NotificationManagerService accepts that
- // for legacy reasons.
- return contentView != null || extras.getBoolean(Builder.EXTRA_REBUILD_CONTENT_VIEW);
- }
-
- /**
- * @hide
- */
public boolean isGroupSummary() {
return mGroupKey != null && (flags & FLAG_GROUP_SUMMARY) != 0;
}
@@ -2125,99 +2128,14 @@
private static final int MAX_ACTION_BUTTONS = 3;
private static final float LARGE_TEXT_SCALE = 1.3f;
- /**
- * @hide
- */
- public static final String EXTRA_NEEDS_REBUILD = "android.rebuild";
-
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_LARGE_ICON = "android.rebuild.largeIcon";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_CONTENT_VIEW = "android.rebuild.contentView";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT =
- "android.rebuild.contentViewActionCount";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW
- = "android.rebuild.bigView";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT
- = "android.rebuild.bigViewActionCount";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW
- = "android.rebuild.hudView";
- /**
- * @hide
- */
- public static final String EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT
- = "android.rebuild.hudViewActionCount";
-
- /**
- * The ApplicationInfo of the package that created the notification, used to create
- * a context to rebuild the notification via a Builder.
- * @hide
- */
- private static final String EXTRA_REBUILD_CONTEXT_APPLICATION_INFO =
- "android.rebuild.applicationInfo";
-
- // Whether to enable stripping (at post time) & rebuilding (at listener receive time) of
- // memory intensive resources.
- private static final boolean STRIP_AND_REBUILD = true;
-
private Context mContext;
-
- private long mWhen;
- private Icon mSmallIcon, mLargeIcon;
- private int mSmallIconLevel;
- private int mNumber;
- private CharSequence mContentTitle;
- private CharSequence mContentText;
- private CharSequence mContentInfo;
- private CharSequence mSubText;
- private PendingIntent mContentIntent;
- private RemoteViews mContentView;
- private PendingIntent mDeleteIntent;
- private PendingIntent mFullScreenIntent;
- private CharSequence mTickerText;
- private RemoteViews mTickerView;
- private Uri mSound;
- private int mAudioStreamType;
- private AudioAttributes mAudioAttributes;
- private long[] mVibrate;
- private int mLedArgb;
- private int mLedOnMs;
- private int mLedOffMs;
- private int mDefaults;
- private int mFlags;
- private int mProgressMax;
- private int mProgress;
- private boolean mProgressIndeterminate;
- private String mCategory;
- private String mGroupKey;
- private String mSortKey;
- private Bundle mExtras;
- private int mPriority;
- private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
- private boolean mUseChronometer;
+ private Notification mN;
+ private Bundle mUserExtras = new Bundle();
private Style mStyle;
- private boolean mShowWhen = true;
- private int mVisibility = VISIBILITY_PRIVATE;
- private Notification mPublicVersion = null;
- private final NotificationColorUtil mColorUtil;
- private ArrayList<String> mPeople;
- private int mColor = COLOR_DEFAULT;
+ private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
+ private ArrayList<String> mPersonList = new ArrayList<String>();
+ private NotificationColorUtil mColorUtil;
+ private boolean mColorUtilInited = false;
private List<Topic> mTopics = new ArrayList<>();
/**
@@ -2226,25 +2144,6 @@
private int mOriginatingUserId;
/**
- * Contains extras related to rebuilding during the build phase.
- */
- private Bundle mRebuildBundle = new Bundle();
- /**
- * Contains the notification to rebuild when this Builder is in "rebuild" mode.
- * Null otherwise.
- */
- private Notification mRebuildNotification = null;
-
- /**
- * Whether the build notification has three lines. This is used to make the top padding for
- * both the contracted and expanded layout consistent.
- *
- * <p>
- * This field is only valid during the build phase.
- */
- private boolean mHasThreeLines;
-
- /**
* Constructs a new Builder with the defaults:
*
@@ -2264,61 +2163,67 @@
* object.
*/
public Builder(Context context) {
- /*
- * Important compatibility note!
- * Some apps out in the wild create a Notification.Builder in their Activity subclass
- * constructor for later use. At this point Activities - themselves subclasses of
- * ContextWrapper - do not have their inner Context populated yet. This means that
- * any calls to Context methods from within this constructor can cause NPEs in existing
- * apps. Any data populated from mContext should therefore be populated lazily to
- * preserve compatibility.
- */
- mContext = context;
-
- // Set defaults to match the defaults of a Notification
- mWhen = System.currentTimeMillis();
- mAudioStreamType = STREAM_DEFAULT;
- mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
- mPriority = PRIORITY_DEFAULT;
- mPeople = new ArrayList<String>();
-
- mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP ?
- NotificationColorUtil.getInstance(mContext) : null;
+ this(context, null);
}
/**
- * Creates a Builder for rebuilding the given Notification.
- * <p>
- * Call {@link #rebuild()} to retrieve the rebuilt version of 'n'.
+ * @hide
*/
- private Builder(Context context, Notification n) {
- this(context);
- mRebuildNotification = n;
- restoreFromNotification(n);
+ public Builder(Context context, Notification toAdopt) {
+ mContext = context;
- Style style = null;
- Bundle extras = n.extras;
- String templateClass = extras.getString(EXTRA_TEMPLATE);
- if (!TextUtils.isEmpty(templateClass)) {
- Class<? extends Style> styleClass = getNotificationStyleClass(templateClass);
- if (styleClass == null) {
- Log.d(TAG, "Unknown style class: " + styleClass);
- return;
+ if (toAdopt == null) {
+ mN = new Notification();
+ mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
+ mN.priority = PRIORITY_DEFAULT;
+ mN.visibility = VISIBILITY_PRIVATE;
+ } else {
+ mN = toAdopt;
+ if (mN.actions != null) {
+ Collections.addAll(mActions, mN.actions);
}
- try {
- Constructor<? extends Style> constructor = styleClass.getConstructor();
- constructor.setAccessible(true);
- style = constructor.newInstance();
- style.restoreFromExtras(extras);
- } catch (Throwable t) {
- Log.e(TAG, "Could not create Style", t);
- return;
+ if (mN.extras.containsKey(EXTRA_PEOPLE)) {
+ Collections.addAll(mPersonList, mN.extras.getStringArray(EXTRA_PEOPLE));
+ }
+
+ if (mN.getTopics() != null) {
+ Collections.addAll(mTopics, mN.getTopics());
+ }
+
+ String templateClass = mN.extras.getString(EXTRA_TEMPLATE);
+ if (!TextUtils.isEmpty(templateClass)) {
+ final Class<? extends Style> styleClass
+ = getNotificationStyleClass(templateClass);
+ if (styleClass == null) {
+ Log.d(TAG, "Unknown style class: " + templateClass);
+ } else {
+ try {
+ final Constructor<? extends Style> ctor = styleClass.getConstructor();
+ ctor.setAccessible(true);
+ final Style style = ctor.newInstance();
+ style.restoreFromExtras(mN.extras);
+
+ if (style != null) {
+ setStyle(style);
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "Could not create Style", t);
+ }
+ }
+ }
+
+ }
+ }
+
+ private NotificationColorUtil getColorUtil() {
+ if (!mColorUtilInited) {
+ mColorUtilInited = true;
+ if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
+ mColorUtil = NotificationColorUtil.getInstance(mContext);
}
}
- if (style != null) {
- setStyle(style);
- }
+ return mColorUtil;
}
/**
@@ -2329,7 +2234,7 @@
* @see Notification#when
*/
public Builder setWhen(long when) {
- mWhen = when;
+ mN.when = when;
return this;
}
@@ -2338,7 +2243,7 @@
* in the content view.
*/
public Builder setShowWhen(boolean show) {
- mShowWhen = show;
+ mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
return this;
}
@@ -2354,7 +2259,7 @@
* @see Notification#when
*/
public Builder setUsesChronometer(boolean b) {
- mUseChronometer = b;
+ mN.extras.putBoolean(EXTRA_SHOW_CHRONOMETER, b);
return this;
}
@@ -2390,7 +2295,7 @@
* @see Notification#iconLevel
*/
public Builder setSmallIcon(@DrawableRes int icon, int level) {
- mSmallIconLevel = level;
+ mN.iconLevel = level;
return setSmallIcon(icon);
}
@@ -2403,7 +2308,10 @@
* @see Notification#icon
*/
public Builder setSmallIcon(Icon icon) {
- mSmallIcon = icon;
+ mN.setSmallIcon(icon);
+ if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
+ mN.icon = icon.getResId();
+ }
return this;
}
@@ -2411,7 +2319,7 @@
* Set the first line of text in the platform notification template.
*/
public Builder setContentTitle(CharSequence title) {
- mContentTitle = safeCharSequence(title);
+ mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
return this;
}
@@ -2419,7 +2327,7 @@
* Set the second line of text in the platform notification template.
*/
public Builder setContentText(CharSequence text) {
- mContentText = safeCharSequence(text);
+ mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
return this;
}
@@ -2429,7 +2337,7 @@
* same location in the standard template.
*/
public Builder setSubText(CharSequence text) {
- mSubText = safeCharSequence(text);
+ mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
return this;
}
@@ -2439,7 +2347,7 @@
* font size for readability.
*/
public Builder setNumber(int number) {
- mNumber = number;
+ mN.number = number;
return this;
}
@@ -2450,7 +2358,7 @@
* right (to the right of a smallIcon if it has been placed there).
*/
public Builder setContentInfo(CharSequence info) {
- mContentInfo = safeCharSequence(info);
+ mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
return this;
}
@@ -2460,19 +2368,52 @@
* The platform template will represent this using a {@link ProgressBar}.
*/
public Builder setProgress(int max, int progress, boolean indeterminate) {
- mProgressMax = max;
- mProgress = progress;
- mProgressIndeterminate = indeterminate;
+ mN.extras.putInt(EXTRA_PROGRESS, progress);
+ mN.extras.putInt(EXTRA_PROGRESS_MAX, max);
+ mN.extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, indeterminate);
return this;
}
/**
* Supply a custom RemoteViews to use instead of the platform template.
*
- * @see Notification#contentView
+ * Use {@link #setCustomContentView(RemoteViews)} instead.
*/
+ @Deprecated
public Builder setContent(RemoteViews views) {
- mContentView = views;
+ return setCustomContentView(views);
+ }
+
+ /**
+ * Supply custom RemoteViews to use instead of the platform template.
+ *
+ * This will override the layout that would otherwise be constructed by this Builder
+ * object.
+ */
+ public Builder setCustomContentView(RemoteViews contentView) {
+ mN.contentView = contentView;
+ return this;
+ }
+
+ /**
+ * Supply custom RemoteViews to use instead of the platform template in the expanded form.
+ *
+ * This will override the expanded layout that would otherwise be constructed by this
+ * Builder object.
+ */
+ public Builder setCustomBigContentView(RemoteViews contentView) {
+ mN.bigContentView = contentView;
+ return this;
+ }
+
+ /**
+ * Supply custom RemoteViews to use instead of the platform template in the heads up dialog.
+ *
+ * This will override the heads-up layout that would otherwise be constructed by this
+ * Builder object.
+ */
+ public Builder setCustomHeadsUpContentView(RemoteViews contentView) {
+ mN.headsUpContentView = contentView;
return this;
}
@@ -2488,7 +2429,7 @@
* @see Notification#contentIntent Notification.contentIntent
*/
public Builder setContentIntent(PendingIntent intent) {
- mContentIntent = intent;
+ mN.contentIntent = intent;
return this;
}
@@ -2498,7 +2439,7 @@
* @see Notification#deleteIntent
*/
public Builder setDeleteIntent(PendingIntent intent) {
- mDeleteIntent = intent;
+ mN.deleteIntent = intent;
return this;
}
@@ -2523,7 +2464,7 @@
* @see Notification#fullScreenIntent
*/
public Builder setFullScreenIntent(PendingIntent intent, boolean highPriority) {
- mFullScreenIntent = intent;
+ mN.fullScreenIntent = intent;
setFlag(FLAG_HIGH_PRIORITY, highPriority);
return this;
}
@@ -2534,7 +2475,7 @@
* @see Notification#tickerText
*/
public Builder setTicker(CharSequence tickerText) {
- mTickerText = safeCharSequence(tickerText);
+ mN.tickerText = safeCharSequence(tickerText);
return this;
}
@@ -2544,8 +2485,8 @@
*/
@Deprecated
public Builder setTicker(CharSequence tickerText, RemoteViews views) {
- mTickerText = safeCharSequence(tickerText);
- mTickerView = views; // we'll save it for you anyway
+ setTicker(tickerText);
+ // views is ignored
return this;
}
@@ -2568,7 +2509,8 @@
* badge atop the large icon).
*/
public Builder setLargeIcon(Icon icon) {
- mLargeIcon = icon;
+ mN.mLargeIcon = icon;
+ mN.extras.putParcelable(EXTRA_LARGE_ICON, icon);
return this;
}
@@ -2585,8 +2527,8 @@
* @see Notification#sound
*/
public Builder setSound(Uri sound) {
- mSound = sound;
- mAudioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
+ mN.sound = sound;
+ mN.audioAttributes = AUDIO_ATTRIBUTES_DEFAULT;
return this;
}
@@ -2603,8 +2545,8 @@
*/
@Deprecated
public Builder setSound(Uri sound, int streamType) {
- mSound = sound;
- mAudioStreamType = streamType;
+ mN.sound = sound;
+ mN.audioStreamType = streamType;
return this;
}
@@ -2619,8 +2561,8 @@
* @see Notification#sound
*/
public Builder setSound(Uri sound, AudioAttributes audioAttributes) {
- mSound = sound;
- mAudioAttributes = audioAttributes;
+ mN.sound = sound;
+ mN.audioAttributes = audioAttributes;
return this;
}
@@ -2637,7 +2579,7 @@
* @see Notification#vibrate
*/
public Builder setVibrate(long[] pattern) {
- mVibrate = pattern;
+ mN.vibrate = pattern;
return this;
}
@@ -2654,9 +2596,9 @@
* @see Notification#ledOffMS
*/
public Builder setLights(@ColorInt int argb, int onMs, int offMs) {
- mLedArgb = argb;
- mLedOnMs = onMs;
- mLedOffMs = offMs;
+ mN.ledARGB = argb;
+ mN.ledOnMS = onMs;
+ mN.ledOffMS = offMs;
return this;
}
@@ -2724,7 +2666,7 @@
* For all default values, use {@link #DEFAULT_ALL}.
*/
public Builder setDefaults(int defaults) {
- mDefaults = defaults;
+ mN.defaults = defaults;
return this;
}
@@ -2734,7 +2676,7 @@
* @see Notification#priority
*/
public Builder setPriority(@Priority int pri) {
- mPriority = pri;
+ mN.priority = pri;
return this;
}
@@ -2744,7 +2686,7 @@
* @see Notification#category
*/
public Builder setCategory(String category) {
- mCategory = category;
+ mN.category = category;
return this;
}
@@ -2771,7 +2713,7 @@
* @see Notification#EXTRA_PEOPLE
*/
public Builder addPerson(String uri) {
- mPeople.add(uri);
+ mPersonList.add(uri);
return this;
}
@@ -2787,7 +2729,7 @@
* @return this object for method chaining
*/
public Builder setGroup(String groupKey) {
- mGroupKey = groupKey;
+ mN.mGroupKey = groupKey;
return this;
}
@@ -2816,7 +2758,7 @@
* @see String#compareTo(String)
*/
public Builder setSortKey(String sortKey) {
- mSortKey = sortKey;
+ mN.mSortKey = sortKey;
return this;
}
@@ -2829,11 +2771,7 @@
*/
public Builder addExtras(Bundle extras) {
if (extras != null) {
- if (mExtras == null) {
- mExtras = new Bundle(extras);
- } else {
- mExtras.putAll(extras);
- }
+ mUserExtras.putAll(extras);
}
return this;
}
@@ -2851,7 +2789,9 @@
* @see Notification#extras
*/
public Builder setExtras(Bundle extras) {
- mExtras = extras;
+ if (extras != null) {
+ mUserExtras = extras;
+ }
return this;
}
@@ -2866,10 +2806,13 @@
* @see Notification#extras
*/
public Bundle getExtras() {
- if (mExtras == null) {
- mExtras = new Bundle();
- }
- return mExtras;
+ return mUserExtras;
+ }
+
+ private Bundle getAllExtras() {
+ final Bundle saveExtras = (Bundle) mUserExtras.clone();
+ saveExtras.putAll(mN.extras);
+ return saveExtras;
}
/**
@@ -2918,6 +2861,21 @@
}
/**
+ * Alter the complete list of actions attached to this notification.
+ * @see #addAction(Action).
+ *
+ * @param actions
+ * @return
+ */
+ public Builder setActions(Action... actions) {
+ mActions.clear();
+ for (int i = 0; i < actions.length; i++) {
+ mActions.add(actions[i]);
+ }
+ return this;
+ }
+
+ /**
* Add a rich notification style to be applied at build time.
*
* @param style Object responsible for modifying the notification style.
@@ -2927,6 +2885,9 @@
mStyle = style;
if (mStyle != null) {
mStyle.setBuilder(this);
+ mN.extras.putString(EXTRA_TEMPLATE, style.getClass().getName());
+ } else {
+ mN.extras.remove(EXTRA_TEMPLATE);
}
}
return this;
@@ -2941,7 +2902,7 @@
* @return The same Builder.
*/
public Builder setVisibility(int visibility) {
- mVisibility = visibility;
+ mN.visibility = visibility;
return this;
}
@@ -2952,7 +2913,12 @@
* @return The same Builder.
*/
public Builder setPublicVersion(Notification n) {
- mPublicVersion = n;
+ if (n != null) {
+ mN.publicVersion = new Notification();
+ n.cloneInto(mN.publicVersion, /*heavy=*/ true);
+ } else {
+ mN.publicVersion = null;
+ }
return this;
}
@@ -2970,9 +2936,9 @@
*/
public void setFlag(int mask, boolean value) {
if (value) {
- mFlags |= mask;
+ mN.flags |= mask;
} else {
- mFlags &= ~mask;
+ mN.flags &= ~mask;
}
}
@@ -2984,7 +2950,7 @@
* @return The same Builder.
*/
public Builder setColor(@ColorInt int argb) {
- mColor = argb;
+ mN.color = argb;
return this;
}
@@ -3075,7 +3041,6 @@
contentView.setViewVisibility(R.id.overflow_divider, View.GONE);
contentView.setViewVisibility(R.id.progress, View.GONE);
contentView.setViewVisibility(R.id.chronometer, View.GONE);
- contentView.setViewVisibility(R.id.time, View.GONE);
}
private RemoteViews applyStandardTemplate(int resId) {
@@ -3093,39 +3058,43 @@
boolean showLine3 = false;
boolean showLine2 = false;
boolean contentTextInLine2 = false;
+ final Bundle ex = mN.extras;
- if (mLargeIcon != null) {
- contentView.setImageViewIcon(R.id.icon, mLargeIcon);
- processLargeLegacyIcon(mLargeIcon, contentView);
- contentView.setImageViewIcon(R.id.right_icon, mSmallIcon);
+ if (mN.mLargeIcon != null) {
+ contentView.setImageViewIcon(R.id.icon, mN.mLargeIcon);
+ processLargeLegacyIcon(mN.mLargeIcon, contentView);
+ contentView.setImageViewIcon(R.id.right_icon, mN.mSmallIcon);
contentView.setViewVisibility(R.id.right_icon, View.VISIBLE);
- processSmallRightIcon(mSmallIcon, contentView);
+ processSmallRightIcon(mN.mSmallIcon, contentView);
} else { // small icon at left
- contentView.setImageViewIcon(R.id.icon, mSmallIcon);
+ contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
contentView.setViewVisibility(R.id.icon, View.VISIBLE);
- processSmallIconAsLarge(mSmallIcon, contentView);
+ processSmallIconAsLarge(mN.mSmallIcon, contentView);
}
- if (mContentTitle != null) {
- contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle));
+ if (ex.getCharSequence(EXTRA_TITLE) != null) {
+ contentView.setTextViewText(R.id.title,
+ processLegacyText(ex.getCharSequence(EXTRA_TITLE)));
}
- if (mContentText != null) {
- contentView.setTextViewText(R.id.text, processLegacyText(mContentText));
+ if (ex.getCharSequence(EXTRA_TEXT) != null) {
+ contentView.setTextViewText(R.id.text,
+ processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
showLine3 = true;
}
- if (mContentInfo != null) {
- contentView.setTextViewText(R.id.info, processLegacyText(mContentInfo));
+ if (ex.getCharSequence(EXTRA_INFO_TEXT) != null) {
+ contentView.setTextViewText(R.id.info,
+ processLegacyText(ex.getCharSequence(EXTRA_INFO_TEXT)));
contentView.setViewVisibility(R.id.info, View.VISIBLE);
showLine3 = true;
- } else if (mNumber > 0) {
+ } else if (mN.number > 0) {
final int tooBig = mContext.getResources().getInteger(
R.integer.status_bar_notification_info_maxnum);
- if (mNumber > tooBig) {
+ if (mN.number > tooBig) {
contentView.setTextViewText(R.id.info, processLegacyText(
mContext.getResources().getString(
R.string.status_bar_notification_info_overflow)));
} else {
NumberFormat f = NumberFormat.getIntegerInstance();
- contentView.setTextViewText(R.id.info, processLegacyText(f.format(mNumber)));
+ contentView.setTextViewText(R.id.info, processLegacyText(f.format(mN.number)));
}
contentView.setViewVisibility(R.id.info, View.VISIBLE);
showLine3 = true;
@@ -3134,10 +3103,12 @@
}
// Need to show three lines?
- if (mSubText != null) {
- contentView.setTextViewText(R.id.text, processLegacyText(mSubText));
- if (mContentText != null) {
- contentView.setTextViewText(R.id.text2, processLegacyText(mContentText));
+ if (ex.getCharSequence(EXTRA_SUB_TEXT) != null) {
+ contentView.setTextViewText(R.id.text,
+ processLegacyText(ex.getCharSequence(EXTRA_SUB_TEXT)));
+ if (ex.getCharSequence(EXTRA_TEXT) != null) {
+ contentView.setTextViewText(R.id.text2,
+ processLegacyText(ex.getCharSequence(EXTRA_TEXT)));
contentView.setViewVisibility(R.id.text2, View.VISIBLE);
showLine2 = true;
contentTextInLine2 = true;
@@ -3146,15 +3117,18 @@
}
} else {
contentView.setViewVisibility(R.id.text2, View.GONE);
- if (hasProgress && (mProgressMax != 0 || mProgressIndeterminate)) {
+ final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
+ final int progress = ex.getInt(EXTRA_PROGRESS, 0);
+ final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
+ if (hasProgress && (max != 0 || ind)) {
contentView.setViewVisibility(R.id.progress, View.VISIBLE);
contentView.setProgressBar(
- R.id.progress, mProgressMax, mProgress, mProgressIndeterminate);
+ R.id.progress, max, progress, ind);
contentView.setProgressBackgroundTintList(
R.id.progress, ColorStateList.valueOf(mContext.getColor(
R.color.notification_progress_background_color)));
- if (mColor != COLOR_DEFAULT) {
- ColorStateList colorStateList = ColorStateList.valueOf(mColor);
+ if (mN.color != COLOR_DEFAULT) {
+ ColorStateList colorStateList = ColorStateList.valueOf(mN.color);
contentView.setProgressTintList(R.id.progress, colorStateList);
contentView.setProgressIndeterminateTintList(R.id.progress, colorStateList);
}
@@ -3170,20 +3144,21 @@
}
if (showsTimeOrChronometer()) {
- if (mUseChronometer) {
+ if (ex.getBoolean(EXTRA_SHOW_CHRONOMETER)) {
contentView.setViewVisibility(R.id.chronometer, View.VISIBLE);
contentView.setLong(R.id.chronometer, "setBase",
- mWhen + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
+ mN.when + (SystemClock.elapsedRealtime() - System.currentTimeMillis()));
contentView.setBoolean(R.id.chronometer, "setStarted", true);
} else {
contentView.setViewVisibility(R.id.time, View.VISIBLE);
- contentView.setLong(R.id.time, "setTime", mWhen);
+ contentView.setLong(R.id.time, "setTime", mN.when);
}
}
// Adjust padding depending on line count and font size.
- contentView.setViewPadding(R.id.line1, 0, calculateTopPadding(mContext,
- mHasThreeLines, mContext.getResources().getConfiguration().fontScale),
+ contentView.setViewPadding(R.id.line1, 0,
+ calculateTopPadding(mContext, hasThreeLines(),
+ mContext.getResources().getConfiguration().fontScale),
0, 0);
// We want to add badge to first line of text.
@@ -3196,7 +3171,8 @@
// Note getStandardView may hide line 3 again.
contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE);
- contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE);
+ contentView.setViewVisibility(R.id.overflow_divider,
+ showLine3 ? View.VISIBLE : View.GONE);
return contentView;
}
@@ -3205,7 +3181,7 @@
* otherwise
*/
private boolean showsTimeOrChronometer() {
- return mWhen != 0 && mShowWhen;
+ return mN.when != 0 && mN.extras.getBoolean(EXTRA_SHOW_WHEN);
}
/**
@@ -3216,15 +3192,19 @@
* is going to have one or two lines
*/
private boolean hasThreeLines() {
- boolean contentTextInLine2 = mSubText != null && mContentText != null;
+ final CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
+ final CharSequence text = mN.extras.getCharSequence(EXTRA_TEXT);
+ boolean contentTextInLine2 = subText != null && text != null;
boolean hasProgress = mStyle == null || mStyle.hasProgress();
// If we have content text in line 2, badge goes into line 2, or line 3 otherwise
boolean badgeInLine3 = getProfileBadgeDrawable() != null && !contentTextInLine2;
- boolean hasLine3 = mContentText != null || mContentInfo != null || mNumber > 0
- || badgeInLine3;
- boolean hasLine2 = (mSubText != null && mContentText != null) ||
- (hasProgress && mSubText == null
- && (mProgressMax != 0 || mProgressIndeterminate));
+ boolean hasLine3 = text != null || mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null
+ || mN.number > 0 || badgeInLine3;
+ final Bundle ex = mN.extras;
+ final int max = ex.getInt(EXTRA_PROGRESS_MAX, 0);
+ final boolean ind = ex.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
+ boolean hasLine2 = (subText != null && text != null) ||
+ (hasProgress && subText == null && (max != 0 || ind));
return hasLine2 && hasLine3;
}
@@ -3271,29 +3251,48 @@
return big;
}
- private RemoteViews makeContentView() {
- if (mContentView != null) {
- return mContentView;
- } else {
- return applyStandardTemplate(getBaseLayoutResource());
+ /**
+ * Construct a RemoteViews for the final 1U notification layout. In order:
+ * 1. Custom contentView from the caller
+ * 2. Style's proposed content view
+ * 3. Standard template view
+ */
+ public RemoteViews makeContentView() {
+ if (mN.contentView != null) {
+ return mN.contentView;
+ } else if (mStyle != null) {
+ final RemoteViews styleView = mStyle.makeContentView();
+ if (styleView != null) {
+ return styleView;
+ }
}
+ return applyStandardTemplate(getBaseLayoutResource());
}
- private RemoteViews makeTickerView() {
- if (mTickerView != null) {
- return mTickerView;
- }
- return null; // tickers are not created by default anymore
- }
-
- private RemoteViews makeBigContentView() {
- if (mActions.size() == 0) return null;
+ /**
+ * Construct a RemoteViews for the final big notification layout.
+ */
+ public RemoteViews makeBigContentView() {
+ if (mStyle != null) {
+ final RemoteViews styleView = mStyle.makeBigContentView();
+ if (styleView != null) {
+ return styleView;
+ }
+ } else if (mActions.size() == 0) return null;
return applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
- private RemoteViews makeHeadsUpContentView() {
- if (mActions.size() == 0) return null;
+ /**
+ * Construct a RemoteViews for the final heads-up notification layout.
+ */
+ public RemoteViews makeHeadsUpContentView() {
+ if (mStyle != null) {
+ final RemoteViews styleView = mStyle.makeHeadsUpContentView();
+ if (styleView != null) {
+ return styleView;
+ }
+ } else if (mActions.size() == 0) return null;
return applyStandardTemplateWithActions(getBigBaseLayoutResource());
}
@@ -3320,11 +3319,11 @@
* doesn't create material notifications by itself) app.
*/
private boolean isLegacy() {
- return mColorUtil != null;
+ return getColorUtil() != null;
}
private void processLegacyAction(Action action, RemoteViews button) {
- if (!isLegacy() || mColorUtil.isGrayscaleIcon(mContext, action.getIcon())) {
+ if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
mContext.getColor(R.color.notification_action_color_filter),
PorterDuff.Mode.MULTIPLY);
@@ -3333,7 +3332,7 @@
private CharSequence processLegacyText(CharSequence charSequence) {
if (isLegacy()) {
- return mColorUtil.invertCharSequenceColors(charSequence);
+ return getColorUtil().invertCharSequenceColors(charSequence);
} else {
return charSequence;
}
@@ -3349,7 +3348,7 @@
PorterDuff.Mode.SRC_ATOP, -1);
applyLargeIconBackground(contentView);
} else {
- if (mColorUtil.isGrayscaleIcon(mContext, largeIcon)) {
+ if (getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
applyLargeIconBackground(contentView);
}
}
@@ -3362,7 +3361,7 @@
// TODO: also check bounds, transparency, that sort of thing.
private void processLargeLegacyIcon(Icon largeIcon, RemoteViews contentView) {
if (largeIcon != null && isLegacy()
- && mColorUtil.isGrayscaleIcon(mContext, largeIcon)) {
+ && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
applyLargeIconBackground(contentView);
} else {
removeLargeIconBackground(contentView);
@@ -3404,7 +3403,7 @@
}
final boolean gray = isLegacy()
&& smallIcon.getType() == Icon.TYPE_RESOURCE
- && mColorUtil.isGrayscaleIcon(mContext, smallIcon.getResId());
+ && getColorUtil().isGrayscaleIcon(mContext, smallIcon.getResId());
if (!isLegacy() || gray) {
contentView.setInt(R.id.right_icon,
"setBackgroundResource",
@@ -3421,17 +3420,17 @@
}
private int sanitizeColor() {
- if (mColor != COLOR_DEFAULT) {
- mColor |= 0xFF000000; // no alpha for custom colors
+ if (mN.color != COLOR_DEFAULT) {
+ mN.color |= 0xFF000000; // no alpha for custom colors
}
- return mColor;
+ return mN.color;
}
private int resolveColor() {
- if (mColor == COLOR_DEFAULT) {
+ if (mN.color == COLOR_DEFAULT) {
return mContext.getColor(R.color.notification_icon_bg_color);
}
- return mColor;
+ return mN.color;
}
/**
@@ -3439,165 +3438,25 @@
* @hide
*/
public Notification buildUnstyled() {
- Notification n = new Notification();
- n.when = mWhen;
- n.mSmallIcon = mSmallIcon;
- if (mSmallIcon != null && mSmallIcon.getType() == Icon.TYPE_RESOURCE) {
- n.icon = mSmallIcon.getResId();
- }
- n.iconLevel = mSmallIconLevel;
- n.number = mNumber;
-
- n.color = sanitizeColor();
-
- setBuilderContentView(n, makeContentView());
- n.contentIntent = mContentIntent;
- n.deleteIntent = mDeleteIntent;
- n.fullScreenIntent = mFullScreenIntent;
- n.tickerText = mTickerText;
- n.tickerView = makeTickerView();
- n.mLargeIcon = mLargeIcon;
- if (mLargeIcon != null && mLargeIcon.getType() == Icon.TYPE_BITMAP) {
- n.largeIcon = mLargeIcon.getBitmap();
- }
- n.sound = mSound;
- n.audioStreamType = mAudioStreamType;
- n.audioAttributes = mAudioAttributes;
- n.vibrate = mVibrate;
- n.ledARGB = mLedArgb;
- n.ledOnMS = mLedOnMs;
- n.ledOffMS = mLedOffMs;
- n.defaults = mDefaults;
- n.flags = mFlags;
- setBuilderBigContentView(n, makeBigContentView());
- setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
- if (mLedOnMs != 0 || mLedOffMs != 0) {
- n.flags |= FLAG_SHOW_LIGHTS;
- }
- if ((mDefaults & DEFAULT_LIGHTS) != 0) {
- n.flags |= FLAG_SHOW_LIGHTS;
- }
- n.category = mCategory;
- n.mGroupKey = mGroupKey;
- n.mSortKey = mSortKey;
- n.priority = mPriority;
if (mActions.size() > 0) {
- n.actions = new Action[mActions.size()];
- mActions.toArray(n.actions);
+ mN.actions = new Action[mActions.size()];
+ mActions.toArray(mN.actions);
}
- n.visibility = mVisibility;
-
- if (mPublicVersion != null) {
- n.publicVersion = new Notification();
- mPublicVersion.cloneInto(n.publicVersion, true);
+ if (!mPersonList.isEmpty()) {
+ mN.extras.putStringArray(EXTRA_PEOPLE,
+ mPersonList.toArray(new String[mPersonList.size()]));
}
if (mTopics.size() > 0) {
- n.topics = new Topic[mTopics.size()];
- mTopics.toArray(n.topics);
+ mN.topics = new Topic[mTopics.size()];
+ mTopics.toArray(mN.topics);
}
- // Note: If you're adding new fields, also update restoreFromNotitification().
- return n;
+ return mN;
}
- /**
- * Capture, in the provided bundle, semantic information used in the construction of
- * this Notification object.
- * @hide
- */
- public void populateExtras(Bundle extras) {
- // Store original information used in the construction of this object
- extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
- extras.putParcelable(EXTRA_REBUILD_CONTEXT_APPLICATION_INFO,
- mContext.getApplicationInfo());
- extras.putCharSequence(EXTRA_TITLE, mContentTitle);
- extras.putCharSequence(EXTRA_TEXT, mContentText);
- extras.putCharSequence(EXTRA_SUB_TEXT, mSubText);
- extras.putCharSequence(EXTRA_INFO_TEXT, mContentInfo);
- extras.putParcelable(EXTRA_SMALL_ICON, mSmallIcon);
- extras.putInt(EXTRA_PROGRESS, mProgress);
- extras.putInt(EXTRA_PROGRESS_MAX, mProgressMax);
- extras.putBoolean(EXTRA_PROGRESS_INDETERMINATE, mProgressIndeterminate);
- extras.putBoolean(EXTRA_SHOW_CHRONOMETER, mUseChronometer);
- extras.putBoolean(EXTRA_SHOW_WHEN, mShowWhen);
- if (mLargeIcon != null) {
- extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
- }
- if (!mPeople.isEmpty()) {
- extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
- }
- // NOTE: If you're adding new extras also update restoreFromNotification().
- }
-
-
- /**
- * @hide
- */
- public static void stripForDelivery(Notification n) {
- if (!STRIP_AND_REBUILD) {
- return;
- }
-
- String templateClass = n.extras.getString(EXTRA_TEMPLATE);
- // Only strip views for known Styles because we won't know how to
- // re-create them otherwise.
- boolean stripViews = TextUtils.isEmpty(templateClass) ||
- getNotificationStyleClass(templateClass) != null;
-
- boolean isStripped = false;
-
- if (n.largeIcon != null && n.extras.containsKey(EXTRA_LARGE_ICON)) {
- // TODO: Would like to check for equality here, but if the notification
- // has been cloned, we can't.
- n.largeIcon = null;
- n.extras.putBoolean(EXTRA_REBUILD_LARGE_ICON, true);
- isStripped = true;
- }
- // Get rid of unmodified BuilderRemoteViews.
-
- if (stripViews &&
- n.contentView instanceof BuilderRemoteViews &&
- n.extras.getInt(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.contentView.getSequenceNumber()) {
- n.contentView = null;
- n.extras.putBoolean(EXTRA_REBUILD_CONTENT_VIEW, true);
- n.extras.remove(EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT);
- isStripped = true;
- }
- if (stripViews &&
- n.bigContentView instanceof BuilderRemoteViews &&
- n.extras.getInt(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.bigContentView.getSequenceNumber()) {
- n.bigContentView = null;
- n.extras.putBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW, true);
- n.extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT);
- isStripped = true;
- }
- if (stripViews &&
- n.headsUpContentView instanceof BuilderRemoteViews &&
- n.extras.getInt(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT, -1) ==
- n.headsUpContentView.getSequenceNumber()) {
- n.headsUpContentView = null;
- n.extras.putBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW, true);
- n.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
- isStripped = true;
- }
-
- if (isStripped) {
- n.extras.putBoolean(EXTRA_NEEDS_REBUILD, true);
- }
- }
-
- /**
- * @hide
- */
- public static Notification rebuild(Context context, Notification n) {
- Bundle extras = n.extras;
- if (!extras.getBoolean(EXTRA_NEEDS_REBUILD)) return n;
- extras.remove(EXTRA_NEEDS_REBUILD);
-
+ public static Notification.Builder recoverBuilder(Context context, Notification n) {
// Re-create notification context so we can access app resources.
- ApplicationInfo applicationInfo = extras.getParcelable(
- EXTRA_REBUILD_CONTEXT_APPLICATION_INFO);
+ ApplicationInfo applicationInfo = n.extras.getParcelable(
+ EXTRA_BUILDER_APPLICATION_INFO);
Context builderContext;
try {
builderContext = context.createApplicationContext(applicationInfo,
@@ -3607,58 +3466,7 @@
builderContext = context; // try with our context
}
- Builder b = new Builder(builderContext, n);
- return b.rebuild();
- }
-
- /**
- * Rebuilds the notification passed in to the rebuild-constructor
- * {@link #Builder(Context, Notification)}.
- *
- * <p>
- * Throws IllegalStateException when invoked on a Builder that isn't in rebuild mode.
- *
- * @hide
- */
- private Notification rebuild() {
- if (mRebuildNotification == null) {
- throw new IllegalStateException("rebuild() only valid when in 'rebuild' mode.");
- }
- mHasThreeLines = hasThreeLines();
-
- Bundle extras = mRebuildNotification.extras;
-
- if (extras.getBoolean(EXTRA_REBUILD_LARGE_ICON)) {
- mRebuildNotification.largeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
- }
- extras.remove(EXTRA_REBUILD_LARGE_ICON);
-
- if (extras.getBoolean(EXTRA_REBUILD_CONTENT_VIEW)) {
- setBuilderContentView(mRebuildNotification, makeContentView());
- if (mStyle != null) {
- mStyle.populateContentView(mRebuildNotification);
- }
- }
- extras.remove(EXTRA_REBUILD_CONTENT_VIEW);
-
- if (extras.getBoolean(EXTRA_REBUILD_BIG_CONTENT_VIEW)) {
- setBuilderBigContentView(mRebuildNotification, makeBigContentView());
- if (mStyle != null) {
- mStyle.populateBigContentView(mRebuildNotification);
- }
- }
- extras.remove(EXTRA_REBUILD_BIG_CONTENT_VIEW);
-
- if (extras.getBoolean(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW)) {
- setBuilderHeadsUpContentView(mRebuildNotification, makeHeadsUpContentView());
- if (mStyle != null) {
- mStyle.populateHeadsUpContentView(mRebuildNotification);
- }
- }
- extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW);
-
- mHasThreeLines = false;
- return mRebuildNotification;
+ return new Builder(builderContext, n);
}
private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
@@ -3674,91 +3482,15 @@
private void setBuilderContentView(Notification n, RemoteViews contentView) {
n.contentView = contentView;
- if (contentView instanceof BuilderRemoteViews) {
- mRebuildBundle.putInt(Builder.EXTRA_REBUILD_CONTENT_VIEW_ACTION_COUNT,
- contentView.getSequenceNumber());
- }
}
private void setBuilderBigContentView(Notification n, RemoteViews bigContentView) {
n.bigContentView = bigContentView;
- if (bigContentView instanceof BuilderRemoteViews) {
- mRebuildBundle.putInt(Builder.EXTRA_REBUILD_BIG_CONTENT_VIEW_ACTION_COUNT,
- bigContentView.getSequenceNumber());
- }
}
private void setBuilderHeadsUpContentView(Notification n,
RemoteViews headsUpContentView) {
n.headsUpContentView = headsUpContentView;
- if (headsUpContentView instanceof BuilderRemoteViews) {
- mRebuildBundle.putInt(Builder.EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT,
- headsUpContentView.getSequenceNumber());
- }
- }
-
- private void restoreFromNotification(Notification n) {
-
- // Notification fields.
- mWhen = n.when;
- mSmallIcon = n.mSmallIcon;
- mSmallIconLevel = n.iconLevel;
- mNumber = n.number;
-
- mColor = n.color;
-
- mContentView = n.contentView;
- mDeleteIntent = n.deleteIntent;
- mFullScreenIntent = n.fullScreenIntent;
- mTickerText = n.tickerText;
- mTickerView = n.tickerView;
- mLargeIcon = n.mLargeIcon;
- mSound = n.sound;
- mAudioStreamType = n.audioStreamType;
- mAudioAttributes = n.audioAttributes;
-
- mVibrate = n.vibrate;
- mLedArgb = n.ledARGB;
- mLedOnMs = n.ledOnMS;
- mLedOffMs = n.ledOffMS;
- mDefaults = n.defaults;
- mFlags = n.flags;
-
- mCategory = n.category;
- mGroupKey = n.mGroupKey;
- mSortKey = n.mSortKey;
- mPriority = n.priority;
- mActions.clear();
- if (n.actions != null) {
- Collections.addAll(mActions, n.actions);
- }
- mVisibility = n.visibility;
-
- mPublicVersion = n.publicVersion;
-
- if (n.topics != null) {
- Collections.addAll(mTopics, n.topics);
- }
-
- // Extras.
- Bundle extras = n.extras;
- mOriginatingUserId = extras.getInt(EXTRA_ORIGINATING_USERID);
- mContentTitle = extras.getCharSequence(EXTRA_TITLE);
- mContentText = extras.getCharSequence(EXTRA_TEXT);
- mSubText = extras.getCharSequence(EXTRA_SUB_TEXT);
- mContentInfo = extras.getCharSequence(EXTRA_INFO_TEXT);
- mProgress = extras.getInt(EXTRA_PROGRESS);
- mProgressMax = extras.getInt(EXTRA_PROGRESS_MAX);
- mProgressIndeterminate = extras.getBoolean(EXTRA_PROGRESS_INDETERMINATE);
- mUseChronometer = extras.getBoolean(EXTRA_SHOW_CHRONOMETER);
- mShowWhen = extras.getBoolean(EXTRA_SHOW_WHEN);
- if (extras.containsKey(EXTRA_LARGE_ICON)) {
- mLargeIcon = extras.getParcelable(EXTRA_LARGE_ICON);
- }
- if (extras.containsKey(EXTRA_PEOPLE)) {
- mPeople.clear();
- Collections.addAll(mPeople, extras.getStringArray(EXTRA_PEOPLE));
- }
}
/**
@@ -3774,38 +3506,23 @@
* object.
*/
public Notification build() {
- if (mSmallIcon != null) {
- mSmallIcon.convertToAshmem();
+ // first, add any extras from the calling code
+ if (mUserExtras != null) {
+ mN.extras = getAllExtras();
}
- if (mLargeIcon != null) {
- mLargeIcon.convertToAshmem();
- }
+
+ // lazy stuff from mContext; see comment in Builder(Context, Notification)
+ mN.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, mContext.getApplicationInfo());
mOriginatingUserId = mContext.getUserId();
- mHasThreeLines = hasThreeLines();
+ mN.extras.putInt(EXTRA_ORIGINATING_USERID, mOriginatingUserId);
- Notification n = buildUnstyled();
+ buildUnstyled();
if (mStyle != null) {
- mStyle.purgeResources();
- n = mStyle.buildStyled(n);
+ mStyle.buildStyled(mN);
}
- if (mExtras != null) {
- n.extras.putAll(mExtras);
- }
-
- if (mRebuildBundle.size() > 0) {
- n.extras.putAll(mRebuildBundle);
- mRebuildBundle.clear();
- }
-
- populateExtras(n.extras);
- if (mStyle != null) {
- mStyle.addExtras(n.extras);
- }
-
- mHasThreeLines = false;
- return n;
+ return mN;
}
/**
@@ -3901,14 +3618,15 @@
checkBuilder();
// Nasty.
- CharSequence oldBuilderContentTitle = mBuilder.mContentTitle;
+ CharSequence oldBuilderContentTitle =
+ mBuilder.getAllExtras().getCharSequence(EXTRA_TITLE);
if (mBigContentTitle != null) {
mBuilder.setContentTitle(mBigContentTitle);
}
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(layoutId);
- mBuilder.mContentTitle = oldBuilderContentTitle;
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TITLE, oldBuilderContentTitle);
if (mBigContentTitle != null && mBigContentTitle.equals("")) {
contentView.setViewVisibility(R.id.line1, View.GONE);
@@ -3919,7 +3637,7 @@
// The last line defaults to the subtext, but can be replaced by mSummaryText
final CharSequence overflowText =
mSummaryTextSet ? mSummaryText
- : mBuilder.mSubText;
+ : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT);
if (overflowText != null) {
contentView.setTextViewText(R.id.text, mBuilder.processLegacyText(overflowText));
contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE);
@@ -3935,6 +3653,31 @@
}
/**
+ * Construct a Style-specific RemoteViews for the final 1U notification layout.
+ * The default implementation has nothing additional to add.
+ * @hide
+ */
+ public RemoteViews makeContentView() {
+ return null;
+ }
+
+ /**
+ * Construct a Style-specific RemoteViews for the final big notification layout.
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
+ return null;
+ }
+
+ /**
+ * Construct a Style-specific RemoteViews for the final HUN layout.
+ * @hide
+ */
+ public RemoteViews makeHeadsUpContentView() {
+ return null;
+ }
+
+ /**
* Changes the padding of the first line such that the big and small content view have the
* same top padding.
*
@@ -3942,12 +3685,13 @@
*/
protected void applyTopPadding(RemoteViews contentView) {
int topPadding = Builder.calculateTopPadding(mBuilder.mContext,
- mBuilder.mHasThreeLines,
+ mBuilder.hasThreeLines(),
mBuilder.mContext.getResources().getConfiguration().fontScale);
contentView.setViewPadding(R.id.line1, 0, topPadding, 0, 0);
}
/**
+ * Apply any style-specific extras to this notification before shipping it out.
* @hide
*/
public void addExtras(Bundle extras) {
@@ -3961,6 +3705,7 @@
}
/**
+ * Reconstruct the internal state of this Style object from extras.
* @hide
*/
protected void restoreFromExtras(Bundle extras) {
@@ -3978,10 +3723,7 @@
* @hide
*/
public Notification buildStyled(Notification wip) {
- populateTickerView(wip);
- populateContentView(wip);
- populateBigContentView(wip);
- populateHeadsUpContentView(wip);
+ addExtras(wip.extras);
return wip;
}
@@ -3990,26 +3732,6 @@
*/
public void purgeResources() {}
- // The following methods are split out so we can re-create notification partially.
- /**
- * @hide
- */
- protected void populateTickerView(Notification wip) {}
- /**
- * @hide
- */
- protected void populateContentView(Notification wip) {}
-
- /**
- * @hide
- */
- protected void populateBigContentView(Notification wip) {}
-
- /**
- * @hide
- */
- protected void populateHeadsUpContentView(Notification wip) {}
-
/**
* Calls {@link android.app.Notification.Builder#build()} on the Builder this Style is
* attached to.
@@ -4115,29 +3837,33 @@
}
}
- private RemoteViews makeBigContentView() {
- // Replace mLargeIcon with mBigLargeIcon if mBigLargeIconSet
+ /**
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
+ // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet
// This covers the following cases:
// 1. mBigLargeIconSet -> mBigLargeIcon (null or non-null) applies, overrides
- // mLargeIcon
- // 2. !mBigLargeIconSet -> mLargeIcon applies
+ // mN.mLargeIcon
+ // 2. !mBigLargeIconSet -> mN.mLargeIcon applies
Icon oldLargeIcon = null;
if (mBigLargeIconSet) {
- oldLargeIcon = mBuilder.mLargeIcon;
- mBuilder.mLargeIcon = mBigLargeIcon;
+ oldLargeIcon = mBuilder.mN.mLargeIcon;
+ mBuilder.mN.mLargeIcon = mBigLargeIcon;
}
RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
if (mBigLargeIconSet) {
- mBuilder.mLargeIcon = oldLargeIcon;
+ mBuilder.mN.mLargeIcon = oldLargeIcon;
}
contentView.setImageViewBitmap(R.id.big_picture, mPicture);
applyTopPadding(contentView);
- boolean twoTextLines = mBuilder.mSubText != null && mBuilder.mContentText != null;
+ boolean twoTextLines = mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT) != null
+ && mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT) != null;
mBuilder.addProfileBadge(contentView,
twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3);
return contentView;
@@ -4168,14 +3894,6 @@
}
mPicture = extras.getParcelable(EXTRA_PICTURE);
}
-
- /**
- * @hide
- */
- @Override
- public void populateBigContentView(Notification wip) {
- mBuilder.setBuilderBigContentView(wip, makeBigContentView());
- }
}
/**
@@ -4255,15 +3973,19 @@
mBigText = extras.getCharSequence(EXTRA_BIG_TEXT);
}
- private RemoteViews makeBigContentView() {
+ /**
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
// Nasty
- CharSequence oldBuilderContentText = mBuilder.mContentText;
- mBuilder.mContentText = null;
+ CharSequence oldBuilderContentText =
+ mBuilder.getAllExtras().getCharSequence(EXTRA_TEXT);
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
- mBuilder.mContentText = oldBuilderContentText;
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText));
contentView.setViewVisibility(R.id.big_text, View.VISIBLE);
@@ -4282,7 +4004,8 @@
private int calculateMaxLines() {
int lineCount = MAX_LINES;
boolean hasActions = mBuilder.mActions.size() > 0;
- boolean hasSummary = (mSummaryTextSet ? mSummaryText : mBuilder.mSubText) != null;
+ boolean hasSummary = (mSummaryTextSet ? mSummaryText
+ : mBuilder.getAllExtras().getCharSequence(EXTRA_SUB_TEXT)) != null;
if (hasActions) {
lineCount -= LINES_CONSUMED_BY_ACTIONS;
}
@@ -4291,19 +4014,11 @@
}
// If we have less top padding at the top, we can fit less lines.
- if (!mBuilder.mHasThreeLines) {
+ if (!mBuilder.hasThreeLines()) {
lineCount--;
}
return lineCount;
}
-
- /**
- * @hide
- */
- @Override
- public void populateBigContentView(Notification wip) {
- mBuilder.setBuilderBigContentView(wip, makeBigContentView());
- }
}
/**
@@ -4384,16 +4099,18 @@
}
}
- private RemoteViews makeBigContentView() {
+ /**
+ * @hide
+ */
+ public RemoteViews makeBigContentView() {
// Remove the content text so line3 disappears unless you have a summary
-
// Nasty
- CharSequence oldBuilderContentText = mBuilder.mContentText;
- mBuilder.mContentText = null;
+ CharSequence oldBuilderContentText = mBuilder.mN.extras.getCharSequence(EXTRA_TEXT);
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, null);
RemoteViews contentView = getStandardView(mBuilder.getInboxLayoutResource());
- mBuilder.mContentText = oldBuilderContentText;
+ mBuilder.getAllExtras().putCharSequence(EXTRA_TEXT, oldBuilderContentText);
contentView.setViewVisibility(R.id.text2, View.GONE);
@@ -4437,14 +4154,6 @@
return contentView;
}
-
- /**
- * @hide
- */
- @Override
- public void populateBigContentView(Notification wip) {
- mBuilder.setBuilderBigContentView(wip, makeBigContentView());
- }
}
/**
@@ -4536,16 +4245,16 @@
* @hide
*/
@Override
- public void populateContentView(Notification wip) {
- mBuilder.setBuilderContentView(wip, makeMediaContentView());
+ public RemoteViews makeContentView() {
+ return makeMediaContentView();
}
/**
* @hide
*/
@Override
- public void populateBigContentView(Notification wip) {
- mBuilder.setBuilderBigContentView(wip, makeMediaBigContentView());
+ public RemoteViews makeBigContentView() {
+ return makeMediaBigContentView();
}
/** @hide */
@@ -4659,7 +4368,7 @@
R.color.notification_media_secondary_color);
contentView.setTextColor(R.id.title, primaryColor);
if (mBuilder.showsTimeOrChronometer()) {
- if (mBuilder.mUseChronometer) {
+ if (mBuilder.getAllExtras().getBoolean(EXTRA_SHOW_CHRONOMETER)) {
contentView.setTextColor(R.id.chronometer, secondaryColor);
} else {
contentView.setTextColor(R.id.time, secondaryColor);
@@ -5503,7 +5212,7 @@
/**
* Gets the accent color.
*
- * @see setColor
+ * @see #setColor
*/
@ColorInt
public int getColor() {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index cb0ff33..f75b22a 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -207,33 +207,7 @@
*/
public void notify(String tag, int id, Notification notification)
{
- int[] idOut = new int[1];
- INotificationManager service = getService();
- String pkg = mContext.getPackageName();
- if (notification.sound != null) {
- notification.sound = notification.sound.getCanonicalUri();
- if (StrictMode.vmFileUriExposureEnabled()) {
- notification.sound.checkFileUriExposed("Notification.sound");
- }
- }
- fixLegacySmallIcon(notification, pkg);
- if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
- if (notification.getSmallIcon() == null) {
- throw new IllegalArgumentException("Invalid notification (no valid small icon): "
- + notification);
- }
- }
- if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
- Notification stripped = notification.clone();
- Builder.stripForDelivery(stripped);
- try {
- service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
- stripped, idOut, UserHandle.myUserId());
- if (id != idOut[0]) {
- Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
- }
- } catch (RemoteException e) {
- }
+ notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
}
/**
@@ -251,12 +225,17 @@
}
}
fixLegacySmallIcon(notification, pkg);
+ if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ if (notification.getSmallIcon() == null) {
+ throw new IllegalArgumentException("Invalid notification (no valid small icon): "
+ + notification);
+ }
+ }
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
- Notification stripped = notification.clone();
- Builder.stripForDelivery(stripped);
+ final Notification copy = notification.clone();
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
- stripped, idOut, user.getIdentifier());
+ copy, idOut, user.getIdentifier());
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
@@ -287,13 +266,7 @@
*/
public void cancel(String tag, int id)
{
- INotificationManager service = getService();
- String pkg = mContext.getPackageName();
- if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
- try {
- service.cancelNotificationWithTag(pkg, tag, id, UserHandle.myUserId());
- } catch (RemoteException e) {
- }
+ cancelAsUser(tag, id, new UserHandle(UserHandle.myUserId()));
}
/**
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/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 11effd0..a1f9743 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6277,6 +6277,15 @@
*/
public static final String FORCE_ALLOW_ON_EXTERNAL = "force_allow_on_external";
+ /**
+ * Whether any activity can be resized. When this is true, any
+ * activity, regardless of manifest values, can be resized for multi-window.
+ * (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES
+ = "force_resizable_activities";
+
/**
* Whether user has enabled development settings.
*/
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index d424546..7e7b5fc 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -31,6 +31,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
@@ -472,9 +473,10 @@
StatusBarNotification sbn = list.get(i);
Notification notification = sbn.getNotification();
try {
- Builder.rebuild(getContext(), notification);
// convert icon metadata to legacy format for older clients
createLegacyIconExtras(notification);
+ // populate remote views for older clients.
+ maybePopulateRemoteViews(notification);
} catch (IllegalArgumentException e) {
if (corruptNotifications == null) {
corruptNotifications = new ArrayList<>(N);
@@ -676,6 +678,18 @@
}
}
+ /**
+ * Populates remote views for pre-N targeting apps.
+ */
+ private void maybePopulateRemoteViews(Notification notification) {
+ if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+ Builder builder = Builder.recoverBuilder(getContext(), notification);
+ notification.contentView = builder.makeContentView();
+ notification.bigContentView = builder.makeBigContentView();
+ notification.headsUpContentView = builder.makeHeadsUpContentView();
+ }
+ }
+
private class INotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
@@ -689,9 +703,10 @@
}
try {
- Notification.Builder.rebuild(getContext(), sbn.getNotification());
+ Notification notification = sbn.getNotification();
// convert icon metadata to legacy format for older clients
createLegacyIconExtras(sbn.getNotification());
+ maybePopulateRemoteViews(sbn.getNotification());
} catch (IllegalArgumentException e) {
// drop corrupt notification
sbn = null;
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index de509b2..2459cfa 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -238,6 +238,7 @@
initialScrollY = Touch.getInitialScrollY(widget, buffer);
}
+ boolean wasTouchSelecting = isTouchSelecting(isMouse, buffer);
boolean handled = Touch.onTouchEvent(widget, buffer, event);
if (widget.didTouchFocusSelect() && !isMouse) {
@@ -267,9 +268,9 @@
// Cursor can be active at any location in the text while mouse pointer can start
// selection from a totally different location. Use LAST_TAP_DOWN span to ensure
// text selection will start from mouse pointer location.
+ final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
if (isMouse && Touch.isSelectionStarted(buffer)) {
- int offset = buffer.getSpanStart(LAST_TAP_DOWN);
- Selection.setSelection(buffer, offset);
+ Selection.setSelection(buffer, startOffset);
}
if (isTouchSelecting(isMouse, buffer) && handled) {
@@ -284,9 +285,9 @@
// Update selection as we're moving the selection area.
// Get the current touch position
- int offset = widget.getOffsetForPosition(event.getX(), event.getY());
-
- Selection.extendSelection(buffer, offset);
+ final int offset = widget.getOffsetForPosition(event.getX(), event.getY());
+ Selection.setSelection(buffer, Math.min(startOffset, offset),
+ Math.max(startOffset, offset));
return true;
}
} else if (action == MotionEvent.ACTION_UP) {
@@ -300,10 +301,12 @@
return true;
}
- int offset = widget.getOffsetForPosition(event.getX(), event.getY());
- if (isTouchSelecting(isMouse, buffer)) {
+ if (wasTouchSelecting) {
+ final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
+ final int endOffset = widget.getOffsetForPosition(event.getX(), event.getY());
+ Selection.setSelection(buffer, Math.min(startOffset, endOffset),
+ Math.max(startOffset, endOffset));
buffer.removeSpan(LAST_TAP_DOWN);
- Selection.extendSelection(buffer, offset);
}
MetaKeyKeyListener.adjustMetaAfterKeypress(buffer);
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index c1eb80d..37d6757 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -343,15 +343,15 @@
int seconds = (int) Math.floor(duration / 1000);
int days = 0, hours = 0, minutes = 0;
- if (seconds > SECONDS_PER_DAY) {
+ if (seconds >= SECONDS_PER_DAY) {
days = seconds / SECONDS_PER_DAY;
seconds -= days * SECONDS_PER_DAY;
}
- if (seconds > SECONDS_PER_HOUR) {
+ if (seconds >= SECONDS_PER_HOUR) {
hours = seconds / SECONDS_PER_HOUR;
seconds -= hours * SECONDS_PER_HOUR;
}
- if (seconds > SECONDS_PER_MINUTE) {
+ if (seconds >= SECONDS_PER_MINUTE) {
minutes = seconds / SECONDS_PER_MINUTE;
seconds -= minutes * SECONDS_PER_MINUTE;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7d39a0c..e17bdd7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4191,7 +4191,10 @@
if (mPointerIconShape != pointerShape) {
mPointerIconShape = pointerShape;
- event.getDevice().setPointerShape(pointerShape);
+ final InputDevice inputDevice = event.getDevice();
+ if (inputDevice != null) {
+ inputDevice.setPointerShape(pointerShape);
+ }
}
} else if (event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) {
mPointerIconShape = PointerIcon.STYLE_NOT_SPECIFIED;
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/core/tests/coretests/src/android/util/TimeUtilsTest.java b/core/tests/coretests/src/android/util/TimeUtilsTest.java
index 74c8e04..2370627 100644
--- a/core/tests/coretests/src/android/util/TimeUtilsTest.java
+++ b/core/tests/coretests/src/android/util/TimeUtilsTest.java
@@ -436,15 +436,17 @@
assertFormatDuration("+100ms", 100);
assertFormatDuration("+101ms", 101);
assertFormatDuration("+330ms", 330);
+ assertFormatDuration("+1s0ms", 1000);
assertFormatDuration("+1s330ms", 1330);
assertFormatDuration("+10s24ms", 10024);
+ assertFormatDuration("+1m0s30ms", 60030);
+ assertFormatDuration("+1h0m0s30ms", 3600030);
+ assertFormatDuration("+1d0h0m0s30ms", 86400030);
}
public void testFormatHugeDuration() {
- //assertFormatDuration("+15542d1h11m11s555ms", 1342833071555L);
- // TODO: improve formatDuration() API
- assertFormatDuration("+999d23h59m59s999ms", 1342833071555L);
- assertFormatDuration("-999d23h59m59s999ms", -1342833071555L);
+ assertFormatDuration("+15542d1h11m11s555ms", 1342833071555L);
+ assertFormatDuration("-15542d1h11m11s555ms", -1342833071555L);
}
private void assertFormatDuration(String expected, long duration) {
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
new file mode 100644
index 0000000..c5e2ae6
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget;
+
+import static android.widget.espresso.TextViewActions.mouseDragOnText;
+import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+import com.android.frameworks.coretests.R;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Tests mouse interaction of the TextView widget from an Activity
+ */
+public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
+
+ public TextViewActivityMouseTest() {
+ super(TextViewActivity.class);
+ }
+
+ @SmallTest
+ public void testSelectTextByDrag() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello world!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ mouseDragOnText(helloWorld.indexOf("llo"), helloWorld.indexOf("ld!")));
+
+ onView(withId(R.id.textview)).check(hasSelection("llo wor"));
+ }
+
+ @SmallTest
+ public void testSelectTextByDrag_reverse() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello world!";
+ onView(withId(R.id.textview)).perform(click());
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(
+ mouseDragOnText( helloWorld.indexOf("ld!"), helloWorld.indexOf("llo")));
+
+ onView(withId(R.id.textview)).check(hasSelection("llo wor"));
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
index a0cd848..9ff8e82 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragOnTextViewActions.java
@@ -20,25 +20,22 @@
import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static com.android.internal.util.Preconditions.checkNotNull;
import static org.hamcrest.Matchers.allOf;
-
import android.annotation.Nullable;
import android.os.SystemClock;
import android.support.test.espresso.UiController;
import android.support.test.espresso.PerformException;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
import android.support.test.espresso.action.MotionEvents;
import android.support.test.espresso.action.PrecisionDescriber;
-import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Swiper;
-import android.support.test.espresso.action.Tap;
import android.support.test.espresso.util.HumanReadables;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.TextView;
+
import org.hamcrest.Matcher;
@@ -51,11 +48,48 @@
* <ul>
*/
public final class DragOnTextViewActions implements ViewAction {
+ public interface Dragger extends Swiper {
+ UiController wrapUiController(UiController uiController);
+ }
/**
* Executes different "drag on text" types to given positions.
*/
- public enum Drag implements Swiper {
+ public enum Drag implements Dragger {
+
+ /**
+ * Starts a drag with a mouse down.
+ */
+ MOUSE_DOWN {
+ private DownMotionPerformer downMotion = new DownMotionPerformer() {
+ @Override
+ public MotionEvent perform(
+ UiController uiController, float[] coordinates, float[] precision) {
+ MotionEvent downEvent = MotionEvents.sendDown(
+ uiController, coordinates, precision)
+ .down;
+ return downEvent;
+ }
+ };
+
+ @Override
+ public Status sendSwipe(
+ UiController uiController,
+ float[] startCoordinates, float[] endCoordinates, float[] precision) {
+ return sendLinearDrag(
+ uiController, downMotion, startCoordinates, endCoordinates, precision);
+ }
+
+ @Override
+ public String toString() {
+ return "mouse down and drag to select";
+ }
+
+ @Override
+ public UiController wrapUiController(UiController uiController) {
+ return new MouseUiController(uiController);
+ }
+ },
/**
* Starts a drag with a long-press.
@@ -197,6 +231,11 @@
return res;
}
+
+ @Override
+ public UiController wrapUiController(UiController uiController) {
+ return uiController;
+ }
}
/**
@@ -215,13 +254,13 @@
MotionEvent perform(UiController uiController, float[] coordinates, float[] precision);
}
- private final Swiper mDragger;
+ private final Dragger mDragger;
private final CoordinatesProvider mStartCoordinatesProvider;
private final CoordinatesProvider mEndCoordinatesProvider;
private final PrecisionDescriber mPrecisionDescriber;
public DragOnTextViewActions(
- Swiper dragger,
+ Dragger dragger,
CoordinatesProvider startCoordinatesProvider,
CoordinatesProvider endCoordinatesProvider,
PrecisionDescriber precisionDescriber) {
@@ -242,6 +281,8 @@
checkNotNull(uiController);
checkNotNull(view);
+ uiController = mDragger.wrapUiController(uiController);
+
float[] startCoordinates = mStartCoordinatesProvider.calculateCoordinates(view);
float[] endCoordinates = mEndCoordinatesProvider.calculateCoordinates(view);
float[] precision = mPrecisionDescriber.describePrecision();
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
new file mode 100644
index 0000000..f1387f8
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.widget.espresso;
+
+import android.support.test.espresso.InjectEventSecurityException;
+import android.support.test.espresso.UiController;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/**
+ * Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE.
+ * Note that this doesn't change the tool type.
+ */
+public class MouseUiController implements UiController {
+ private final UiController mUiController;
+
+ public MouseUiController(UiController uiController) {
+ mUiController = uiController;
+ }
+
+ @Override
+ public boolean injectKeyEvent(KeyEvent event) throws InjectEventSecurityException {
+ return mUiController.injectKeyEvent(event);
+ }
+
+ @Override
+ public boolean injectMotionEvent(MotionEvent event) throws InjectEventSecurityException {
+ // Modify the event to mimic mouse primary button event.
+ event.setSource(InputDevice.SOURCE_MOUSE);
+ event.setButtonState(MotionEvent.BUTTON_PRIMARY);
+ return mUiController.injectMotionEvent(event);
+ }
+
+ @Override
+ public boolean injectString(String str) throws InjectEventSecurityException {
+ return mUiController.injectString(str);
+ }
+
+ @Override
+ public void loopMainThreadForAtLeast(long millisDelay) {
+ mUiController.loopMainThreadForAtLeast(millisDelay);
+ }
+
+ @Override
+ public void loopMainThreadUntilIdle() {
+ mUiController.loopMainThreadUntilIdle();
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 835b1b9..4f5a72b 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -124,6 +124,27 @@
}
/**
+ * Returns an action that click then drags by mouse on text from startIndex to endIndex on the
+ * TextView.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a TextView displayed on screen
+ * <ul>
+ *
+ * @param startIndex The index of the TextView's text to start a drag from
+ * @param endIndex The index of the TextView's text to end the drag at
+ */
+ public static ViewAction mouseDragOnText(int startIndex, int endIndex) {
+ return actionWithAssertions(
+ new DragOnTextViewActions(
+ DragOnTextViewActions.Drag.MOUSE_DOWN,
+ new TextCoordinates(startIndex),
+ new TextCoordinates(endIndex),
+ Press.PINPOINT));
+ }
+
+ /**
* A provider of the x, y coordinates of the text at the specified index in a text view.
*/
private static final class TextCoordinates implements CoordinatesProvider {
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index 3bd17fa..abcccbd 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -99,7 +99,7 @@
/** @hide */
public void printShortString(PrintWriter pw) {
- pw.println("["); pw.print(x); pw.print(","); pw.print(y); pw.print("]");
+ pw.print("["); pw.print(x); pw.print(","); pw.print(y); pw.print("]");
}
/**
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 94806ca..7dbba25 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -25,6 +25,15 @@
namespace android {
namespace uirenderer {
+void BakedOpRenderer::Info::setViewport(uint32_t width, uint32_t height) {
+ viewportWidth = width;
+ viewportHeight = height;
+ orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
+
+ renderState.setViewport(width, height);
+ renderState.blend().syncEnabled();
+}
+
Texture* BakedOpRenderer::Info::getTexture(const SkBitmap* bitmap) {
Texture* texture = renderState.assetAtlas().getEntryTexture(bitmap);
if (!texture) {
@@ -45,9 +54,54 @@
didDraw = true;
}
-void BakedOpRenderer::startFrame(Info& info) {
- info.renderState.setViewport(info.viewportWidth, info.viewportHeight);
- info.renderState.blend().syncEnabled();
+Layer* BakedOpRenderer::startLayer(Info& info, uint32_t width, uint32_t height) {
+ info.caches.textureState().activateTexture(0);
+ Layer* layer = info.caches.layerCache.get(info.renderState, width, height);
+ LOG_ALWAYS_FATAL_IF(!layer, "need layer...");
+
+ info.layer = layer;
+ layer->texCoords.set(0.0f, width / float(layer->getHeight()),
+ height / float(layer->getWidth()), 0.0f);
+
+ layer->setFbo(info.renderState.genFramebuffer());
+ info.renderState.bindFramebuffer(layer->getFbo());
+ layer->bindTexture();
+
+ // Initialize the texture if needed
+ if (layer->isEmpty()) {
+ layer->allocateTexture();
+ layer->setEmpty(false);
+ }
+
+ // attach the texture to the FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ layer->getTextureId(), 0);
+ LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED");
+ LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
+ "framebuffer incomplete!");
+
+ // Clear the FBO
+ info.renderState.scissor().setEnabled(false);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Change the viewport & ortho projection
+ info.setViewport(width, height);
+ return layer;
+}
+
+void BakedOpRenderer::endLayer(Info& info) {
+ Layer* layer = info.layer;
+ info.layer = nullptr;
+
+ // Detach the texture from the FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+ LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
+ layer->removeFbo(false);
+}
+
+void BakedOpRenderer::startFrame(Info& info, uint32_t width, uint32_t height) {
+ info.renderState.bindFramebuffer(0);
+ info.setViewport(width, height);
Caches::getInstance().clearGarbage();
if (!info.opaque) {
@@ -130,7 +184,31 @@
}
void BakedOpRenderer::onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) {
- LOG_ALWAYS_FATAL("unsupported operation");
+ Layer* layer = *op.layerHandle;
+
+ // TODO: make this work for HW layers
+ layer->setPaint(op.paint);
+ layer->setBlend(true);
+ float layerAlpha = (layer->getAlpha() / 255.0f) * state.alpha;
+
+ const bool tryToSnap = state.computedState.transform.isPureTranslate();
+ Glop glop;
+ GlopBuilder(info.renderState, info.caches, &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshTexturedUvQuad(nullptr, layer->texCoords)
+ .setFillLayer(layer->getTexture(), layer->getColorFilter(), layerAlpha, layer->getMode(), Blend::ModeOrderSwap::NoSwap)
+ .setTransform(state.computedState.transform, TransformFlags::None)
+ .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
+ .build();
+ info.renderGlop(state, glop);
+
+ // return layer to cache, since each clipped savelayer is only drawn once.
+ layer->setConvexMask(nullptr);
+ if (!info.caches.layerCache.put(layer)) {
+ // Failing to add the layer to the cache should happen only if the layer is too large
+ LAYER_LOGD("Deleting layer");
+ layer->decStrong(nullptr);
+ }
}
} // namespace uirenderer
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index f45dbe4..616adde 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -25,21 +25,21 @@
class Caches;
struct Glop;
+class Layer;
class RenderState;
class BakedOpRenderer {
public:
class Info {
public:
- Info(Caches& caches, RenderState& renderState, int viewportWidth, int viewportHeight, bool opaque)
+ Info(Caches& caches, RenderState& renderState, bool opaque)
: renderState(renderState)
, caches(caches)
- , opaque(opaque)
- , viewportWidth(viewportWidth)
- , viewportHeight(viewportHeight) {
- orthoMatrix.loadOrtho(viewportWidth, viewportHeight);
+ , opaque(opaque) {
}
+ void setViewport(uint32_t width, uint32_t height);
+
Texture* getTexture(const SkBitmap* bitmap);
void renderGlop(const BakedOpState& state, const Glop& glop);
@@ -47,16 +47,19 @@
Caches& caches;
bool didDraw = false;
- bool opaque;
+ Layer* layer = nullptr;
// where should these live? layer state object?
- int viewportWidth;
- int viewportHeight;
+ bool opaque;
+ uint32_t viewportWidth = 0;
+ uint32_t viewportHeight = 0;
Matrix4 orthoMatrix;
};
- static void startFrame(Info& info);
+ static Layer* startLayer(Info& info, uint32_t width, uint32_t height);
+ static void endLayer(Info& info);
+ static void startFrame(Info& info, uint32_t width, uint32_t height);
static void endFrame(Info& info);
/**
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index f99d92b..489ebc1 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -155,8 +155,7 @@
if (fbo) {
if (flush) LayerRenderer::flushLayer(renderState, this);
- // If put fails the cache will delete the FBO
- caches.fboCache.put(fbo);
+ renderState.deleteFramebuffer(fbo);
fbo = 0;
}
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 227271d..e9e5d81 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -189,7 +189,7 @@
LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
Caches& caches = Caches::getInstance();
- GLuint fbo = caches.fboCache.get();
+ GLuint fbo = renderState.genFramebuffer();
if (!fbo) {
ALOGW("Could not obtain an FBO");
return nullptr;
@@ -204,7 +204,7 @@
// We first obtain a layer before comparing against the max texture size
// because layers are not allocated at the exact desired size. They are
- // always created slighly larger to improve recycling
+ // always created slightly larger to improve recycling
const uint32_t maxTextureSize = caches.maxTextureSize;
if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) {
ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
@@ -357,7 +357,7 @@
&& bitmap->width() <= caches.maxTextureSize
&& bitmap->height() <= caches.maxTextureSize) {
- GLuint fbo = caches.fboCache.get();
+ GLuint fbo = renderState.getFramebuffer();
if (!fbo) {
ALOGW("Could not obtain an FBO");
return false;
@@ -465,7 +465,7 @@
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
caches.textureState().deleteTexture(texture);
- caches.fboCache.put(fbo);
+ renderState.deleteFramebuffer(fbo);
renderState.setViewport(previousViewportWidth, previousViewportHeight);
return status;
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index c1417c4..cde42f8 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -203,7 +203,7 @@
};
// iterate back toward target to see if anything drawn since should overlap the new op
-// if no target, merging ops still interate to find similar batch to insert after
+// if no target, merging ops still iterate to find similar batch to insert after
void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
BatchBase** targetBatch, size_t* insertBatchIndex) const {
for (int i = mBatches.size() - 1; i >= 0; i--) {
@@ -292,18 +292,14 @@
}
}
-OpReorderer::OpReorderer()
+OpReorderer::OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
+ const std::vector< sp<RenderNode> >& nodes)
: mCanvasState(*this) {
- mLayerReorderers.emplace_back();
+ ATRACE_NAME("prepare drawing commands");
+
+ mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
mLayerStack.push_back(0);
-}
-void OpReorderer::onViewportInitialized() {}
-
-void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-
-void OpReorderer::defer(const SkRect& clip, int viewportWidth, int viewportHeight,
- const std::vector< sp<RenderNode> >& nodes) {
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
Vector3());
@@ -321,13 +317,22 @@
}
}
-void OpReorderer::defer(int viewportWidth, int viewportHeight, const DisplayList& displayList) {
+OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList)
+ : mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
+
+ mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
+ mLayerStack.push_back(0);
+
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
0, 0, viewportWidth, viewportHeight, Vector3());
deferImpl(displayList);
}
+void OpReorderer::onViewportInitialized() {}
+
+void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+
/**
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
*
@@ -350,11 +355,6 @@
void OpReorderer::replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) {
ATRACE_NAME("flush drawing commands");
- // Relay through layers in reverse order, since layers
- // later in the list will be drawn by earlier ones
- for (int i = mLayerReorderers.size() - 1; i >= 0; i--) {
- mLayerReorderers[i].replayBakedOpsImpl(arg, receivers);
- }
}
void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
@@ -405,15 +405,17 @@
// TODO: test rejection at defer time, where the bounds become empty
void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+ const uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
+ const uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
+
mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
mCanvasState.writableSnapshot()->transform->loadIdentity();
- mCanvasState.writableSnapshot()->initializeViewport(
- (int) op.unmappedBounds.getWidth(), (int) op.unmappedBounds.getHeight());
+ mCanvasState.writableSnapshot()->initializeViewport(layerWidth, layerHeight);
mCanvasState.writableSnapshot()->roundRectClipState = nullptr;
// create a new layer, and push its index on the stack
mLayerStack.push_back(mLayerReorderers.size());
- mLayerReorderers.emplace_back();
+ mLayerReorderers.emplace_back(layerWidth, layerHeight);
mLayerReorderers.back().beginLayerOp = &op;
}
@@ -432,7 +434,8 @@
beginLayerOp.unmappedBounds,
beginLayerOp.localMatrix,
beginLayerOp.localClipRect,
- beginLayerOp.paint);
+ beginLayerOp.paint,
+ &mLayerReorderers[finishedLayerIndex].layer);
BakedOpState* bakedOpState = tryBakeOpState(*drawLayerOp);
if (bakedOpState) {
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 73dc9af..f32b858 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -63,6 +63,10 @@
*/
class LayerReorderer {
public:
+ LayerReorderer(uint32_t width, uint32_t height)
+ : width(width)
+ , height(height) {}
+
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still iterate to find similar batch to insert after
void locateInsertIndex(int batchId, const Rect& clippedBounds,
@@ -77,15 +81,22 @@
void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers) const;
+ bool empty() const {
+ return mBatches.empty();
+ }
+
void clear() {
mBatches.clear();
}
void dump() const;
+ Layer* layer = nullptr;
const BeginLayerOp* beginLayerOp = nullptr;
-
+ const uint32_t width;
+ const uint32_t height;
private:
+
std::vector<BatchBase*> mBatches;
/**
@@ -100,14 +111,13 @@
};
public:
- OpReorderer();
- virtual ~OpReorderer() {}
-
// TODO: not final, just presented this way for simplicity. Layers too?
- void defer(const SkRect& clip, int viewportWidth, int viewportHeight,
+ OpReorderer(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes);
- void defer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
+ OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList);
+
+ virtual ~OpReorderer() {}
/**
* replayBakedOps() is templated based on what class will receive ops being replayed.
@@ -128,8 +138,21 @@
static BakedOpReceiver receivers[] = {
MAP_OPS(BAKED_OP_RECEIVER)
};
- StaticReceiver::startFrame(arg);
- replayBakedOpsImpl((void*)&arg, receivers);
+
+ // Relay through layers in reverse order, since layers
+ // later in the list will be drawn by earlier ones
+ for (int i = mLayerReorderers.size() - 1; i >= 1; i--) {
+ LayerReorderer& layer = mLayerReorderers[i];
+ if (!layer.empty()) {
+ layer.layer = StaticReceiver::startLayer(arg, layer.width, layer.height);
+ layer.replayBakedOpsImpl((void*)&arg, receivers);
+ StaticReceiver::endLayer(arg);
+ }
+ }
+
+ const LayerReorderer& fbo0 = mLayerReorderers[0];
+ StaticReceiver::startFrame(arg, fbo0.width, fbo0.height);
+ fbo0.replayBakedOpsImpl((void*)&arg, receivers);
StaticReceiver::endFrame(arg);
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index d4f65b6..8c3603b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -673,7 +673,7 @@
bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
layer->clipRect.set(clip);
- layer->setFbo(mCaches.fboCache.get());
+ layer->setFbo(mRenderState.genFramebuffer());
writableSnapshot()->region = &writableSnapshot()->layer->region;
writableSnapshot()->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer;
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index dd01637..6c31b42 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -29,6 +29,7 @@
namespace android {
namespace uirenderer {
+class Layer;
class RenderNode;
struct Vertex;
@@ -136,8 +137,12 @@
};
struct LayerOp : RecordedOp {
- LayerOp(BASE_PARAMS)
- : SUPER(LayerOp) {}
+ LayerOp(BASE_PARAMS, Layer** layerHandle)
+ : SUPER(LayerOp)
+ , layerHandle(layerHandle) {}
+ // Records a handle to the Layer object, since the Layer itself won't be
+ // constructed until after this operation is constructed.
+ Layer** layerHandle;
};
}; // namespace uirenderer
diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp
index cf96d44..382c0bd 100644
--- a/libs/hwui/microbench/OpReordererBench.cpp
+++ b/libs/hwui/microbench/OpReordererBench.cpp
@@ -48,8 +48,7 @@
void BM_OpReorderer_defer::Run(int iters) {
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer;
- reorderer.defer(200, 200, *sReorderingDisplayList);
+ OpReorderer reorderer(200, 200, *sReorderingDisplayList);
MicroBench::DoNotOptimize(&reorderer);
}
StopBenchmarkTiming();
@@ -60,11 +59,10 @@
TestUtils::runOnRenderThread([this, iters](RenderState& renderState, Caches& caches) {
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer;
- reorderer.defer(200, 200, *sReorderingDisplayList);
+ OpReorderer reorderer(200, 200, *sReorderingDisplayList);
MicroBench::DoNotOptimize(&reorderer);
- BakedOpRenderer::Info info(caches, renderState, 200, 200, true);
+ BakedOpRenderer::Info info(caches, renderState, true);
reorderer.replayBakedOps<BakedOpRenderer>(info);
}
StopBenchmarkTiming();
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index dfa70ac..9637117 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -125,6 +125,21 @@
}
}
+GLuint RenderState::genFramebuffer() {
+ GLuint ret;
+ glGenFramebuffers(1, &ret);
+ return ret;
+}
+
+void RenderState::deleteFramebuffer(GLuint fbo) {
+ if (mFramebuffer == fbo) {
+ // GL defines that deleting the currently bound FBO rebinds FBO 0.
+ // Reflect this in our cached value.
+ mFramebuffer = 0;
+ }
+ glDeleteFramebuffers(1, &fbo);
+}
+
void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
if (mode == DrawGlInfo::kModeProcessNoContext) {
// If there's no context we don't need to interrupt as there's
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 9ae0845..87a7996 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -57,7 +57,10 @@
void getViewport(GLsizei* outWidth, GLsizei* outHeight);
void bindFramebuffer(GLuint fbo);
- GLint getFramebuffer() { return mFramebuffer; }
+ GLuint getFramebuffer() { return mFramebuffer; }
+ GLuint genFramebuffer();
+ void deleteFramebuffer(GLuint fbo);
+
void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f571426..7c0f0b6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -318,10 +318,8 @@
mEglManager.damageFrame(frame, dirty);
#if HWUI_NEW_OPS
- OpReorderer reorderer;
- reorderer.defer(dirty, frame.width(), frame.height(), mRenderNodes);
- BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(),
- frame.width(), frame.height(), mOpaque);
+ OpReorderer reorderer(dirty, frame.width(), frame.height(), mRenderNodes);
+ BakedOpRenderer::Info info(Caches::getInstance(), mRenderThread.renderState(), mOpaque);
// TODO: profiler().draw(mCanvas);
reorderer.replayBakedOps<BakedOpRenderer>(info);
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index b353ae6..485759b 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -333,7 +333,7 @@
if (CC_LIKELY(err == EGL_SUCCESS)) {
return true;
}
- if (err == EGL_BAD_SURFACE) {
+ if (err == EGL_BAD_SURFACE || err == EGL_BAD_NATIVE_WINDOW) {
// For some reason our surface was destroyed out from under us
// This really shouldn't happen, but if it does we can recover easily
// by just not trying to use the surface anymore
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
index 891af91..2eefd37 100644
--- a/libs/hwui/tests/TreeContentAnimation.cpp
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -54,15 +54,10 @@
}
};
-static TestCanvas* startRecording(RenderNode* node) {
- TestCanvas* renderer = new TestCanvas(
- node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
- return renderer;
-}
-
-static void endRecording(TestCanvas* renderer, RenderNode* node) {
- node->setStagingDisplayList(renderer->finishRecording());
- delete renderer;
+static void recordNode(RenderNode& node, std::function<void(TestCanvas&)> contentCallback) {
+ TestCanvas canvas(node.stagingProperties().getWidth(), node.stagingProperties().getHeight());
+ contentCallback(canvas);
+ node.setStagingDisplayList(canvas.finishRecording());
}
class TreeContentAnimation {
@@ -75,7 +70,7 @@
frameCount = fc;
}
}
- virtual void createContent(int width, int height, TestCanvas* renderer) = 0;
+ virtual void createContent(int width, int height, TestCanvas* canvas) = 0;
virtual void doFrame(int frameNr) = 0;
template <class T>
@@ -108,11 +103,9 @@
proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
- android::uirenderer::Rect DUMMY;
-
- TestCanvas* renderer = startRecording(rootNode);
- animation.createContent(width, height, renderer);
- endRecording(renderer, rootNode);
+ recordNode(*rootNode, [&animation, width, height](TestCanvas& canvas) {
+ animation.createContent(width, height, &canvas); //TODO: no&
+ });
// Do a few cold runs then reset the stats so that the caches are all hot
for (int i = 0; i < 3; i++) {
@@ -140,19 +133,19 @@
class ShadowGridAnimation : public TreeContentAnimation {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->insertReorderBarrier(true);
for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- renderer->drawRenderNode(card.get());
+ canvas->drawRenderNode(card.get());
cards.push_back(card);
}
}
- renderer->insertReorderBarrier(false);
+ canvas->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
@@ -171,9 +164,9 @@
node->mutateStagingProperties().mutableOutline().setShouldClip(true);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
- TestCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
+ recordNode(*node, [](TestCanvas& canvas) {
+ canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ });
return node;
}
};
@@ -187,19 +180,19 @@
class ShadowGrid2Animation : public TreeContentAnimation {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->insertReorderBarrier(true);
for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
- renderer->drawRenderNode(card.get());
+ canvas->drawRenderNode(card.get());
cards.push_back(card);
}
}
- renderer->insertReorderBarrier(false);
+ canvas->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
@@ -218,9 +211,9 @@
node->mutateStagingProperties().mutableOutline().setShouldClip(true);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
- TestCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
+ recordNode(*node, [](TestCanvas& canvas) {
+ canvas.drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ });
return node;
}
};
@@ -233,15 +226,30 @@
class RectGridAnimation : public TreeContentAnimation {
public:
- sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ sp<RenderNode> card = new RenderNode();
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->insertReorderBarrier(true);
- card = createCard(40, 40, 200, 200);
- renderer->drawRenderNode(card.get());
+ card->mutateStagingProperties().setLeftTopRightBottom(50, 50, 250, 250);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ recordNode(*card, [](TestCanvas& canvas) {
+ canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(false);
+ SkRegion region;
+ for (int xOffset = 0; xOffset < 200; xOffset+=2) {
+ for (int yOffset = 0; yOffset < 200; yOffset+=2) {
+ region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+ }
+ }
+
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ canvas.drawRegion(region, paint);
+ });
+ canvas->drawRenderNode(card.get());
+
+ canvas->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
@@ -249,29 +257,6 @@
card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- TestCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
-
- SkRegion region;
- for (int xOffset = 0; xOffset < width; xOffset+=2) {
- for (int yOffset = 0; yOffset < height; yOffset+=2) {
- region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
- }
- }
-
- SkPaint paint;
- paint.setColor(0xff00ffff);
- renderer->drawRegion(region, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
};
static Benchmark _RectGrid(BenchmarkInfo{
"rectgrid",
@@ -282,15 +267,22 @@
class OvalAnimation : public TreeContentAnimation {
public:
- sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+ sp<RenderNode> card = new RenderNode();
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->insertReorderBarrier(true);
- card = createCard(40, 40, 400, 400);
- renderer->drawRenderNode(card.get());
+ card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ recordNode(*card, [](TestCanvas& canvas) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ canvas.drawOval(0, 0, 200, 200, paint);
+ });
+ canvas->drawRenderNode(card.get());
- renderer->insertReorderBarrier(false);
+ canvas->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
@@ -299,22 +291,6 @@
card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- TestCanvas* renderer = startRecording(node.get());
-
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(0xFF000000);
- renderer->drawOval(0, 0, width, height, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
};
static Benchmark _Oval(BenchmarkInfo{
"oval",
@@ -325,7 +301,7 @@
class PartialDamageTest : public TreeContentAnimation {
public:
std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas* renderer) override {
+ void createContent(int width, int height, TestCanvas* canvas) override {
static SkColor COLORS[] = {
0xFFF44336,
0xFF9C27B0,
@@ -333,13 +309,13 @@
0xFF4CAF50,
};
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
COLORS[static_cast<int>((y / dp(116))) % 4]);
- renderer->drawRenderNode(card.get());
+ canvas->drawRenderNode(card.get());
cards.push_back(card);
}
}
@@ -350,10 +326,10 @@
cards[0]->mutateStagingProperties().setTranslationY(curFrame);
cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- TestCanvas* renderer = startRecording(cards[0].get());
- renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
- SkXfermode::kSrcOver_Mode);
- endRecording(renderer, cards[0].get());
+ recordNode(*cards[0], [curFrame](TestCanvas& canvas) {
+ canvas.drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+ SkXfermode::kSrcOver_Mode);
+ });
}
static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
@@ -378,9 +354,9 @@
node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- TestCanvas* renderer = startRecording(node.get());
- renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
+ recordNode(*node, [color](TestCanvas& canvas) {
+ canvas.drawColor(color, SkXfermode::kSrcOver_Mode);
+ });
return node;
}
};
@@ -393,16 +369,24 @@
});
-class SimpleRectGridAnimation : public TreeContentAnimation {
+class SaveLayerAnimation : public TreeContentAnimation {
public:
- sp<RenderNode> card;
- void createContent(int width, int height, TestCanvas* renderer) override {
- SkPaint paint;
- paint.setColor(0xFF00FFFF);
- renderer->drawRect(0, 0, width, height, paint);
+ sp<RenderNode> card = new RenderNode();
+ void createContent(int width, int height, TestCanvas* canvas) override {
+ canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
- card = createCard(40, 40, 200, 200);
- renderer->drawRenderNode(card.get());
+ card->mutateStagingProperties().setLeftTopRightBottom(0, 0, 200, 200);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ recordNode(*card, [](TestCanvas& canvas) {
+ canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped
+ canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
+ canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode); // inner, clipped
+ canvas.restore();
+ canvas.restore();
+ });
+
+ canvas->drawRenderNode(card.get());
}
void doFrame(int frameNr) override {
int curFrame = frameNr % 150;
@@ -410,24 +394,10 @@
card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- TestCanvas* renderer = startRecording(node.get());
- SkPaint paint;
- paint.setColor(0xFFFF00FF);
- renderer->drawRect(0, 0, width, height, paint);
-
- endRecording(renderer, node.get());
- return node;
- }
};
-static Benchmark _SimpleRectGrid(BenchmarkInfo{
- "simplerectgrid",
- "A simple collection of rects. "
- "Low CPU/GPU load.",
- TreeContentAnimation::run<SimpleRectGridAnimation>
+static Benchmark _SaveLayer(BenchmarkInfo{
+ "savelayer",
+ "A nested pair of clipped saveLayer operations. "
+ "Tests the clipped saveLayer codepath. Draws content into offscreen buffers and back again.",
+ TreeContentAnimation::run<SaveLayerAnimation>
});
diff --git a/libs/hwui/unit_tests/BakedOpStateTests.cpp b/libs/hwui/unit_tests/BakedOpStateTests.cpp
index 82aebea..bc1b69f 100644
--- a/libs/hwui/unit_tests/BakedOpStateTests.cpp
+++ b/libs/hwui/unit_tests/BakedOpStateTests.cpp
@@ -85,13 +85,5 @@
}
}
-#define UNSUPPORTED_OP(Info, Type) \
- static void on##Type(Info*, const Type&, const BakedOpState&) { FAIL(); }
-
-class Info {
-public:
- int index = 0;
-};
-
}
}
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index d02f89d..af9c3f2 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -35,19 +35,22 @@
* for every test.
*
* onXXXOp methods fail by default - tests should override ops they expect
+ * startLayer fails by default - tests should override if expected
* startFrame/endFrame do nothing by default - tests should override to intercept
*/
template<class CustomClient, class Arg>
class TestReceiver {
public:
#define CLIENT_METHOD(Type) \
- virtual void on##Type(Arg&, const Type&, const BakedOpState&) { FAIL(); }
+ virtual void on##Type(Arg&, const Type&, const BakedOpState&) { ADD_FAILURE(); }
class Client {
public:
virtual ~Client() {};
MAP_OPS(CLIENT_METHOD)
- virtual void startFrame(Arg& info) {}
+ virtual Layer* startLayer(Arg& info, uint32_t width, uint32_t height) { ADD_FAILURE(); return nullptr; }
+ virtual void endLayer(Arg& info) { ADD_FAILURE(); }
+ virtual void startFrame(Arg& info, uint32_t width, uint32_t height) {}
virtual void endFrame(Arg& info) {}
};
@@ -57,11 +60,18 @@
}
MAP_OPS(DISPATCHER_METHOD)
- static void startFrame(Arg& info) {
+ static Layer* startLayer(Arg& info, uint32_t width, uint32_t height) {
CustomClient client;
- client.startFrame(info);
+ return client.startLayer(info, width, height);
}
-
+ static void endLayer(Arg& info) {
+ CustomClient client;
+ client.endLayer(info);
+ }
+ static void startFrame(Arg& info, uint32_t width, uint32_t height) {
+ CustomClient client;
+ client.startFrame(info, width, height);
+ }
static void endFrame(Arg& info) {
CustomClient client;
client.endFrame(info);
@@ -78,8 +88,10 @@
class SimpleReceiver : public TestReceiver<SimpleReceiver, Info>::Client {
public:
- void startFrame(Info& info) override {
+ void startFrame(Info& info, uint32_t width, uint32_t height) override {
EXPECT_EQ(0, info.index++);
+ EXPECT_EQ(100u, width);
+ EXPECT_EQ(200u, height);
}
void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
EXPECT_EQ(1, info.index++);
@@ -97,8 +109,7 @@
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
- OpReorderer reorderer;
- reorderer.defer(200, 200, *dl);
+ OpReorderer reorderer(100, 200, *dl);
Info info;
reorderer.replayBakedOps<TestReceiver<SimpleReceiver, Info>>(info);
@@ -113,8 +124,7 @@
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
- OpReorderer reorderer;
- reorderer.defer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl);
Info info;
reorderer.replayBakedOps<TestReceiver<FailReceiver, Info>>(info);
@@ -146,8 +156,7 @@
canvas.restore();
});
- OpReorderer reorderer;
- reorderer.defer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl);
Info info;
reorderer.replayBakedOps<TestReceiver<SimpleBatchingReceiver, Info>>(info);
@@ -167,7 +176,7 @@
EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
break;
default:
- FAIL();
+ ADD_FAILURE();
}
}
};
@@ -196,8 +205,7 @@
std::vector< sp<RenderNode> > nodes;
nodes.push_back(parent.get());
- OpReorderer reorderer;
- reorderer.defer(SkRect::MakeWH(200, 200), 200, 200, nodes);
+ OpReorderer reorderer(SkRect::MakeWH(200, 200), 200, 200, nodes);
Info info;
reorderer.replayBakedOps<TestReceiver<RenderNodeReceiver, Info>>(info);
@@ -221,8 +229,7 @@
std::vector< sp<RenderNode> > nodes;
nodes.push_back(node.get());
- OpReorderer reorderer;
- reorderer.defer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
+ OpReorderer reorderer(SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
200, 200, nodes);
Info info;
@@ -232,8 +239,17 @@
class SaveLayerSimpleReceiver : public TestReceiver<SaveLayerSimpleReceiver, Info>::Client {
public:
- void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ Layer* startLayer(Info& info, uint32_t width, uint32_t height) override {
EXPECT_EQ(0, info.index++);
+ EXPECT_EQ(180u, width);
+ EXPECT_EQ(180u, height);
+ return nullptr;
+ }
+ void endLayer(Info& info) override {
+ EXPECT_EQ(2, info.index++);
+ }
+ void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, info.index++);
EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
@@ -243,7 +259,7 @@
EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
}
void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, info.index++);
+ EXPECT_EQ(3, info.index++);
EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
EXPECT_TRUE(state.computedState.transform.isIdentity());
@@ -256,33 +272,61 @@
canvas.restore();
});
- OpReorderer reorderer;
- reorderer.defer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl);
Info info;
reorderer.replayBakedOps<TestReceiver<SaveLayerSimpleReceiver, Info>>(info);
- EXPECT_EQ(2, info.index);
+ EXPECT_EQ(4, info.index);
}
-// saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as rect2, rect1, layerOp2, layerOp1
+/* saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as:
+ * - startLayer2, rect2 endLayer2
+ * - startLayer1, rect1, drawLayer2, endLayer1
+ * - startFrame, layerOp1, endFrame
+ */
class SaveLayerNestedReceiver : public TestReceiver<SaveLayerNestedReceiver, Info>::Client {
public:
- void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ Layer* startLayer(Info& info, uint32_t width, uint32_t height) override {
const int index = info.index++;
if (index == 0) {
+ EXPECT_EQ(400u, width);
+ EXPECT_EQ(400u, height);
+ return (Layer*) 0x400;
+ } else if (index == 3) {
+ EXPECT_EQ(800u, width);
+ EXPECT_EQ(800u, height);
+ return (Layer*) 0x800;
+ } else { ADD_FAILURE(); }
+ return (Layer*) nullptr;
+ }
+ void endLayer(Info& info) override {
+ int index = info.index++;
+ EXPECT_TRUE(index == 2 || index == 6);
+ }
+ void startFrame(Info& info, uint32_t width, uint32_t height) override {
+ EXPECT_EQ(7, info.index++);
+ }
+ void endFrame(Info& info) override {
+ EXPECT_EQ(9, info.index++);
+ }
+ void onRectOp(Info& info, const RectOp& op, const BakedOpState& state) override {
+ const int index = info.index++;
+ if (index == 1) {
EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
- } else if (index == 1) {
+ } else if (index == 4) {
EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
- } else { FAIL(); }
+ } else { ADD_FAILURE(); }
}
void onLayerOp(Info& info, const LayerOp& op, const BakedOpState& state) override {
const int index = info.index++;
- if (index == 2) {
+ if (index == 5) {
+ EXPECT_EQ((Layer*)0x400, *op.layerHandle);
EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
- } else if (index == 3) {
+ } else if (index == 8) {
+ EXPECT_EQ((Layer*)0x800, *op.layerHandle);
EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
- } else { FAIL(); }
+ } else { ADD_FAILURE(); }
}
};
TEST(OpReorderer, saveLayerNested) {
@@ -299,12 +343,11 @@
canvas.restore();
});
- OpReorderer reorderer;
- reorderer.defer(800, 800, *dl);
+ OpReorderer reorderer(800, 800, *dl);
Info info;
reorderer.replayBakedOps<TestReceiver<SaveLayerNestedReceiver, Info>>(info);
- EXPECT_EQ(4, info.index);
+ EXPECT_EQ(10, info.index);
}
TEST(OpReorderer, saveLayerContentRejection) {
@@ -319,8 +362,7 @@
canvas.restore();
canvas.restore();
});
- OpReorderer reorderer;
- reorderer.defer(200, 200, *dl);
+ OpReorderer reorderer(200, 200, *dl);
Info info;
// should see no ops, even within the layer, since the layer should be rejected
diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
index c023123..e8cdf46 100644
--- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp
+++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp
@@ -38,7 +38,7 @@
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
canvas.restore();
});
- playbackOps(*dl, [](const RecordedOp& op) { FAIL(); });
+ playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
}
TEST(RecordingCanvas, testSimpleRectRecord) {
@@ -135,7 +135,7 @@
// TODO: add asserts
break;
default:
- FAIL();
+ ADD_FAILURE();
}
});
EXPECT_EQ(3, count);
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index bf3387b..bca9be6 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -257,11 +257,13 @@
int deg = Integer.parseInt(degrees);
double min;
double sec = 0.0;
+ boolean secPresent = false;
if (st.hasMoreTokens()) {
min = Integer.parseInt(minutes);
String seconds = st.nextToken();
sec = Double.parseDouble(seconds);
+ secPresent = true;
} else {
min = Double.parseDouble(minutes);
}
@@ -273,11 +275,15 @@
if ((deg < 0.0) || (deg > 179 && !isNegative180)) {
throw new IllegalArgumentException("coordinate=" + coordinate);
}
- if (min < 0 || min > 59) {
+
+ // min must be in [0, 59] if seconds are present, otherwise [0.0, 60.0)
+ if (min < 0 || min >= 60 || (secPresent && (min > 59))) {
throw new IllegalArgumentException("coordinate=" +
coordinate);
}
- if (sec < 0 || sec > 59) {
+
+ // sec must be in [0.0, 60.0)
+ if (sec < 0 || sec >= 60) {
throw new IllegalArgumentException("coordinate=" +
coordinate);
}
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index 45fb579..93dbf3f 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -180,7 +180,8 @@
Log.e(TAG, "could not open device " + info);
} else {
...
- }, new Handler(Looper.getMainLooper())
+ }
+ }, new Handler(Looper.getMainLooper())
);
</pre>
diff --git a/opengl/java/android/opengl/GLES31.java b/opengl/java/android/opengl/GLES31.java
index 3cbaa60..805930e 100644
--- a/opengl/java/android/opengl/GLES31.java
+++ b/opengl/java/android/opengl/GLES31.java
@@ -24,9 +24,14 @@
public static final int GL_VERTEX_SHADER_BIT = 0x00000001;
public static final int GL_FRAGMENT_SHADER_BIT = 0x00000002;
+ public static final int GL_COMPUTE_SHADER_BIT = 0x00000020;
+ public static final int GL_ALL_SHADER_BITS = -1; // 0xFFFFFFFF
+
+ public static final int GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT = 0x00000001;
+ public static final int GL_ELEMENT_ARRAY_BARRIER_BIT = 0x00000002;
public static final int GL_UNIFORM_BARRIER_BIT = 0x00000004;
public static final int GL_TEXTURE_FETCH_BARRIER_BIT = 0x00000008;
- public static final int GL_COMPUTE_SHADER_BIT = 0x00000020;
+ public static final int GL_SHADER_IMAGE_ACCESS_BARRIER_BIT = 0x00000020;
public static final int GL_COMMAND_BARRIER_BIT = 0x00000040;
public static final int GL_PIXEL_BUFFER_BARRIER_BIT = 0x00000080;
public static final int GL_TEXTURE_UPDATE_BARRIER_BIT = 0x00000100;
@@ -35,7 +40,8 @@
public static final int GL_TRANSFORM_FEEDBACK_BARRIER_BIT = 0x00000800;
public static final int GL_ATOMIC_COUNTER_BARRIER_BIT = 0x00001000;
public static final int GL_SHADER_STORAGE_BARRIER_BIT = 0x00002000;
- public static final int GL_ALL_SHADER_BITS = -1; // 0xFFFFFFFF
+ public static final int GL_ALL_BARRIER_BITS = -1; // 0xFFFFFFFF
+
public static final int GL_TEXTURE_WIDTH = 0x1000;
public static final int GL_TEXTURE_HEIGHT = 0x1001;
diff --git a/packages/DocumentsUI/res/layout/item_doc_list.xml b/packages/DocumentsUI/res/layout/item_doc_list.xml
index 085df35..eba00a6 100644
--- a/packages/DocumentsUI/res/layout/item_doc_list.xml
+++ b/packages/DocumentsUI/res/layout/item_doc_list.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<com.android.documentsui.DocListItem xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.documentsui.ListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/item_doc_list_background"
@@ -137,4 +137,4 @@
</LinearLayout>
-</com.android.documentsui.DocListItem>
+</com.android.documentsui.ListItem>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index 73538278..ad419aa 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -16,6 +16,6 @@
<resources>
<bool name="productivity_device">true</bool>
- <!-- intentionally unset. Vendors should set this in an overlay. -->
+ <!-- Intentionally unset. Vendors should set this in an overlay. -->
<string name="trusted_quick_viewer_package"></string>
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 3b985ec..627ba75 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -334,12 +334,12 @@
startActivity(intent);
return;
} catch (SecurityException e) {
- // carry on to regular view mode.
+ // Carry on to regular view mode.
Log.e(TAG, "Caught security error: " + e.getLocalizedMessage());
}
}
- // fallback to traditional VIEW action...
+ // Fallback to traditional VIEW action...
intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(doc.derivedUri);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index 6a424a6..605c530 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -92,6 +92,8 @@
intent.setClipData(mClipData);
return intent;
+ } else {
+ Log.e(TAG, "Can't resolve trusted quick view package: " + trustedPkg);
}
}
diff --git a/packages/DocumentsUI/tests/Android.mk b/packages/DocumentsUI/tests/Android.mk
index 3f191a9..2a540d4 100644
--- a/packages/DocumentsUI/tests/Android.mk
+++ b/packages/DocumentsUI/tests/Android.mk
@@ -3,11 +3,12 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
+#LOCAL_SDK_VERSION := current
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target guava
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target guava ub-uiautomator
LOCAL_PACKAGE_NAME := DocumentsUITests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
@@ -15,3 +16,4 @@
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)
+
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
new file mode 100644
index 0000000..1f4b751
--- /dev/null
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.view.MotionEvent;
+
+import java.util.concurrent.TimeoutException;
+
+public class FilesActivityUiTest extends InstrumentationTestCase {
+
+ private static final String TAG = "FilesActivityUiTest";
+ private static final String TARGET_PKG = "com.android.documentsui";
+ private static final String LAUNCHER_PKG = "com.android.launcher";
+ private static final int ONE_SECOND = 1000;
+ private static final int FIVE_SECONDS = 5 * ONE_SECOND;
+
+ private ActionBar mBar;
+ private UiDevice mDevice;
+ private Context mContext;
+
+ public void setUp() throws TimeoutException {
+ // Initialize UiDevice instance.
+ mDevice = UiDevice.getInstance(getInstrumentation());
+
+ Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);
+
+ // Start from the home screen.
+ mDevice.pressHome();
+ mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), FIVE_SECONDS);
+
+ // Launch app.
+ mContext = getInstrumentation().getContext();
+ Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mContext.startActivity(intent);
+
+ // Wait for the app to appear.
+ mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), FIVE_SECONDS);
+ mDevice.waitForIdle();
+
+ mBar = new ActionBar();
+ }
+
+ public void testSwitchMode() throws Exception {
+ UiObject2 mode = mBar.gridMode(100);
+ if (mode != null) {
+ mode.click();
+ assertNotNull(mBar.listMode(ONE_SECOND));
+ } else {
+ mBar.listMode(100).click();
+ assertNotNull(mBar.gridMode(ONE_SECOND));
+ }
+ }
+
+ private class ActionBar {
+
+ public UiObject2 gridMode(int timeout) {
+ // Note that we're using By.desc rather than By.res, because of b/25285770
+ BySelector selector = By.desc("Grid view");
+ if (timeout > 0) {
+ mDevice.wait(Until.findObject(selector), timeout);
+ }
+ return mDevice.findObject(selector);
+ }
+
+ public UiObject2 listMode(int timeout) {
+ // Note that we're using By.desc rather than By.res, because of b/25285770
+ BySelector selector = By.desc("List view");
+ if (timeout > 0) {
+ mDevice.wait(Until.findObject(selector), timeout);
+ }
+ return mDevice.findObject(selector);
+ }
+ }
+}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 11e937b..df9d44a 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -2,6 +2,7 @@
import android.content.ContentValues;
import android.content.Context;
+import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
@@ -30,10 +31,9 @@
private static final String TABLE_MTP_DOCUMENTS = "MtpDocuments";
- static final String COLUMN_DEVICE_ID = "deviceId";
- static final String COLUMN_STORAGE_ID = "storageId";
- static final String COLUMN_OBJECT_HANDLE = "objectHandle";
- static final String COLUMN_FULL_PATH = "fullPath";
+ static final String COLUMN_DEVICE_ID = "device_id";
+ static final String COLUMN_STORAGE_ID = "storage_id";
+ static final String COLUMN_OBJECT_HANDLE = "object_handle";
private static class OpenHelper extends SQLiteOpenHelper {
private static final String CREATE_TABLE_QUERY =
@@ -43,7 +43,6 @@
COLUMN_DEVICE_ID + " INTEGER NOT NULL," +
COLUMN_STORAGE_ID + " INTEGER NOT NULL," +
COLUMN_OBJECT_HANDLE + " INTEGER," +
- COLUMN_FULL_PATH + " TEXT NOT NULL," +
DocumentsContract.Document.COLUMN_MIME_TYPE + " TEXT," +
DocumentsContract.Document.COLUMN_DISPLAY_NAME + " TEXT NOT NULL," +
DocumentsContract.Document.COLUMN_SUMMARY + " TEXT," +
@@ -86,17 +85,15 @@
}
@VisibleForTesting
- void putRootDocument(MtpRoot root) throws Exception {
+ void putRootDocument(Resources resources, MtpRoot root) throws Exception {
database.beginTransaction();
try {
final ContentValues values = new ContentValues();
values.put(COLUMN_DEVICE_ID, root.mDeviceId);
values.put(COLUMN_STORAGE_ID, root.mStorageId);
values.putNull(COLUMN_OBJECT_HANDLE);
- values.put(
- COLUMN_FULL_PATH, "/" + root.mDeviceId + "/" + escape(root.mDescription));
values.put(Document.COLUMN_MIME_TYPE, DocumentsContract.Document.MIME_TYPE_DIR);
- values.put(Document.COLUMN_DISPLAY_NAME, root.mDescription);
+ values.put(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources));
values.putNull(Document.COLUMN_SUMMARY);
values.putNull(Document.COLUMN_LAST_MODIFIED);
values.putNull(Document.COLUMN_ICON);
@@ -113,7 +110,7 @@
}
@VisibleForTesting
- void putDocument(int deviceId, String parentFullPath, MtpObjectInfo info) throws Exception {
+ void putDocument(int deviceId, MtpObjectInfo info) throws Exception {
database.beginTransaction();
try {
final String mimeType = CursorHelper.formatTypeToMimeType(info.getFormat());
@@ -134,9 +131,7 @@
values.put(COLUMN_DEVICE_ID, deviceId);
values.put(COLUMN_STORAGE_ID, info.getStorageId());
values.put(COLUMN_OBJECT_HANDLE, info.getObjectHandle());
- values.put(COLUMN_FULL_PATH, parentFullPath + "/" + escape(info.getName()));
- values.put(
- Document.COLUMN_MIME_TYPE, CursorHelper.formatTypeToMimeType(info.getFormat()));
+ values.put(Document.COLUMN_MIME_TYPE, mimeType);
values.put(Document.COLUMN_DISPLAY_NAME, info.getName());
values.putNull(Document.COLUMN_SUMMARY);
values.put(
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 7ce32542..8e1335f 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -1,6 +1,5 @@
package com.android.mtp;
-
import android.database.Cursor;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
@@ -15,7 +14,6 @@
MtpDatabase.COLUMN_DEVICE_ID,
MtpDatabase.COLUMN_STORAGE_ID,
MtpDatabase.COLUMN_OBJECT_HANDLE,
- MtpDatabase.COLUMN_FULL_PATH,
DocumentsContract.Document.COLUMN_MIME_TYPE,
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
DocumentsContract.Document.COLUMN_SUMMARY,
@@ -25,6 +23,8 @@
DocumentsContract.Document.COLUMN_SIZE
};
+ private final TestResources resources = new TestResources();
+
@Override
public void tearDown() {
MtpDatabase.deleteDatabase(getContext());
@@ -32,35 +32,10 @@
public void testPutRootDocument() throws Exception {
final MtpDatabase database = new MtpDatabase(getContext());
- final MtpRoot root = new MtpRoot(
- 0,
- 1,
- "Device A",
- "Storage",
- 1000,
- 2000,
- "");
- database.putRootDocument(root);
-
- final MtpRoot duplicatedNameRoot = new MtpRoot(
- 0,
- 2,
- "Device A",
- "Storage",
- 1000,
- 2000,
- "");
- database.putRootDocument(duplicatedNameRoot);
-
- final MtpRoot strangeNameRoot = new MtpRoot(
- 0,
- 3,
- "Device A",
- "/@#%&<>Storage",
- 1000,
- 2000,
- "");
- database.putRootDocument(strangeNameRoot);
+ database.putRootDocument(resources, new MtpRoot(0, 1, "Device", "Storage", 1000, 2000, ""));
+ database.putRootDocument(resources, new MtpRoot(0, 2, "Device", "Storage", 1000, 2000, ""));
+ database.putRootDocument(
+ resources, new MtpRoot(0, 3, "Device", "/@#%&<>Storage", 1000, 2000, ""));
final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES);
assertEquals(3, cursor.getCount());
@@ -70,22 +45,23 @@
assertEquals("deviceId", 0, cursor.getInt(1));
assertEquals("storageId", 1, cursor.getInt(2));
assertTrue("objectHandle", cursor.isNull(3));
- assertEquals("fullPath", "/0/Storage", cursor.getString(4));
- assertEquals("mimeType", DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(5));
- assertEquals("displayName", "Storage", cursor.getString(6));
- assertTrue("summary", cursor.isNull(7));
- assertTrue("lastModified", cursor.isNull(8));
- assertTrue("icon", cursor.isNull(9));
- assertEquals("flag", 0, cursor.getInt(10));
- assertEquals("size", 1000, cursor.getInt(11));
+ assertEquals("mimeType", DocumentsContract.Document.MIME_TYPE_DIR, cursor.getString(4));
+ assertEquals("displayName", "Device Storage", cursor.getString(5));
+ assertTrue("summary", cursor.isNull(6));
+ assertTrue("lastModified", cursor.isNull(7));
+ assertTrue("icon", cursor.isNull(8));
+ assertEquals("flag", 0, cursor.getInt(9));
+ assertEquals("size", 1000, cursor.getInt(10));
cursor.moveToNext();
assertEquals("documentId", 2, cursor.getInt(0));
- assertEquals("fullPath", "/0/Storage", cursor.getString(4));
+ assertEquals("displayName", "Device Storage", cursor.getString(5));
cursor.moveToNext();
assertEquals("documentId", 3, cursor.getInt(0));
- assertEquals("fullPath", "/0/%2F%40%23%25%26%3C%3EStorage", cursor.getString(4));
+ assertEquals("displayName", "Device /@#%&<>Storage", cursor.getString(5));
+
+ cursor.close();
}
public void testPutDocument() throws Exception {
@@ -96,7 +72,7 @@
builder.setStorageId(5);
builder.setFormat(MtpConstants.FORMAT_TEXT);
builder.setCompressedSize(1000);
- database.putDocument(0, "/0/Storage", builder.build());
+ database.putDocument(0, builder.build());
final Cursor cursor = database.queryChildDocuments(COLUMN_NAMES);
assertEquals(1, cursor.getCount());
@@ -105,17 +81,16 @@
assertEquals("deviceId", 0, cursor.getInt(1));
assertEquals("storageId", 5, cursor.getInt(2));
assertEquals("objectHandle", 100, cursor.getInt(3));
- assertEquals("fullPath", "/0/Storage/test.txt", cursor.getString(4));
- assertEquals("mimeType", "text/plain", cursor.getString(5));
- assertEquals("displayName", "test.txt", cursor.getString(6));
- assertTrue("summary", cursor.isNull(7));
- assertTrue("lastModified", cursor.isNull(8));
- assertTrue("icon", cursor.isNull(9));
+ assertEquals("mimeType", "text/plain", cursor.getString(4));
+ assertEquals("displayName", "test.txt", cursor.getString(5));
+ assertTrue("summary", cursor.isNull(6));
+ assertTrue("lastModified", cursor.isNull(7));
+ assertTrue("icon", cursor.isNull(8));
assertEquals(
"flag",
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
- cursor.getInt(10));
- assertEquals("size", 1000, cursor.getInt(11));
+ cursor.getInt(9));
+ assertEquals("size", 1000, cursor.getInt(10));
}
}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 0c03814c..5765f0a 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -16,9 +16,6 @@
package com.android.mtp;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
import android.database.Cursor;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
@@ -26,7 +23,6 @@
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract;
import android.test.AndroidTestCase;
-import android.test.mock.MockResources;
import android.test.suitebuilder.annotation.SmallTest;
import java.io.FileNotFoundException;
@@ -39,16 +35,7 @@
private TestContentResolver mResolver;
private MtpDocumentsProvider mProvider;
private TestMtpManager mMtpManager;
- private final MockResources mResources = new MockResources() {
- @Override
- public String getString(int id) throws NotFoundException {
- switch (id) {
- case R.string.root_name:
- return "%1$s %2$s";
- }
- throw new NotFoundException();
- }
- };
+ private final TestResources mResources = new TestResources();
@Override
public void setUp() throws IOException {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
new file mode 100644
index 0000000..eb80e3b
--- /dev/null
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResources.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mtp;
+
+import android.test.mock.MockResources;
+
+class TestResources extends MockResources {
+ @Override
+ public String getString(int id) throws NotFoundException {
+ switch (id) {
+ case R.string.root_name:
+ return "%1$s %2$s";
+ }
+ throw new NotFoundException();
+ }
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 9b1f103..c44a7960 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -225,4 +225,10 @@
<!-- [CHAR LIMIT=NONE] Label of a running process that represents another user -->
<string name="running_process_item_user_label">User: <xliff:g id="user_name">%1$s</xliff:g></string>
+
+ <!-- Launch defaults preference summary with some set [CHAR LIMIT=40] -->
+ <string name="launch_defaults_some">Some defaults set</string>
+ <!-- Launch defaults preference summary with none set [CHAR LIMIT=40] -->
+ <string name="launch_defaults_none">No defaults set</string>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
new file mode 100644
index 0000000..344bf62
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.settingslib.applications;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.hardware.usb.IUsbManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppUtils {
+ private static final String TAG = "AppUtils";
+
+ public static CharSequence getLaunchByDefaultSummary(ApplicationsState.AppEntry appEntry,
+ IUsbManager usbManager, PackageManager pm, Context context) {
+ String packageName = appEntry.info.packageName;
+ boolean hasPreferred = hasPreferredActivities(pm, packageName)
+ || hasUsbDefaults(usbManager, packageName);
+ int status = pm.getIntentVerificationStatus(packageName, UserHandle.myUserId());
+ // consider a visible current link-handling state to be any explicitly designated behavior
+ boolean hasDomainURLsPreference =
+ status != PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ return context.getString(hasPreferred || hasDomainURLsPreference
+ ? R.string.launch_defaults_some
+ : R.string.launch_defaults_none);
+ }
+
+ public static boolean hasUsbDefaults(IUsbManager usbManager, String packageName) {
+ try {
+ if (usbManager != null) {
+ return usbManager.hasDefaults(packageName, UserHandle.myUserId());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "mUsbManager.hasDefaults", e);
+ }
+ return false;
+ }
+
+ public static boolean hasPreferredActivities(PackageManager pm, String packageName) {
+ // Get list of preferred activities
+ List<ComponentName> prefActList = new ArrayList<>();
+ // Intent list cannot be null. so pass empty list
+ List<IntentFilter> intentList = new ArrayList<>();
+ pm.getPreferredActivities(intentList, prefActList, packageName);
+ Log.d(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
+ return prefActList.size() > 0;
+ }
+
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3a5680d..7c86f96 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -999,9 +999,7 @@
<!-- Screen pinning dialog title. -->
<string name="screen_pinning_title">Screen is pinned</string>
<!-- Screen pinning dialog description. -->
- <string name="screen_pinning_description">This keeps it in view until you unpin. Touch and hold Back and Overview at the same time to unpin.</string>
- <!-- Screen pinning dialog description when in accessibility mode. -->
- <string name="screen_pinning_description_accessible">This keeps it in view until you unpin. Touch and hold Overview to unpin.</string>
+ <string name="screen_pinning_description">This keeps it in view until you unpin. Touch and hold Back to unpin.</string>
<!-- Screen pinning positive response. -->
<string name="screen_pinning_positive">Got it</string>
<!-- Screen pinning negative response. -->
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 1d0bfe7..9a4cd93 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -27,4 +27,9 @@
void cancelPreloadingRecents();
void showNextAffiliatedTask();
void showPrevAffiliatedTask();
+
+ /**
+ * Docks the top-most task and opens recents.
+ */
+ void dockTopTask();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index c216f97..9400108 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -284,6 +284,11 @@
}
@Override
+ public void dockTopTask() {
+ mImpl.dockTopTask();
+ }
+
+ @Override
public void showNextAffiliatedTask() {
mImpl.showNextAffiliatedTask();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index e2e0e918..76b666f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -56,7 +56,7 @@
/** Returns whether the status bar scrim should be animated when shown for the first time. */
public boolean shouldAnimateStatusBarScrim() {
- return launchedFromHome;
+ return true;
}
/** Returns whether the status bar scrim should be visible. */
@@ -72,6 +72,6 @@
/** Returns whether the nav bar scrim should be visible. */
public boolean hasNavBarScrim() {
// Only show the scrim if we have recent tasks, and if the nav bar is not transposed
- return !launchedWithNoRecentTasks && mConfig.hasTransposedNavBar;
+ return !launchedWithNoRecentTasks && !mConfig.hasTransposedNavBar;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7192b7a..2f0030c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -418,6 +418,16 @@
showRelativeAffiliatedTask(false);
}
+ public void dockTopTask() {
+ SystemServicesProxy ssp = Recents.getSystemServices();
+ ActivityManager.RunningTaskInfo topTask = ssp.getTopMostTask();
+ if (topTask != null && !ssp.isInHomeStack(topTask.id)) {
+ ssp.moveTaskToDockedStack(topTask.id,
+ ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT);
+ showRecents(false /* triggeredFromAltTab */);
+ }
+ }
+
/**
* Returns the preloaded load plan and invalidates it.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 31ee8ad..28299d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -234,14 +234,6 @@
dismissAllowingStateLoss();
mRecentsActivity.dismissRecentsToHomeWithoutTransitionAnimation();
- // In debug mode, we force all task to be resizeable regardless of the
- // current app configuration.
- for (int i = additionalTasks; i >= 0; --i) {
- if (mTasks[i] != null) {
- ssp.setTaskResizeable(mTasks[i].key.id);
- }
- }
-
// Show tasks as they might not be currently visible - beginning with the oldest so that
// the focus ends on the selected one.
for (int i = additionalTasks; i >= 0; --i) {
@@ -277,8 +269,7 @@
if (mTasks[0].key.stackId != DOCKED_STACK_ID) {
int taskId = mTasks[0].key.id;
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.setTaskResizeable(taskId);
- ssp.dockTask(taskId, createMode);
+ ssp.startTaskInDockedMode(taskId, createMode);
mRecentsView.launchTask(mTasks[0], null, DOCKED_STACK_ID);
} else {
Toast.makeText(getContext(), "Already docked", Toast.LENGTH_SHORT);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 231843e..10075bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -214,11 +214,8 @@
.setVisibility(View.INVISIBLE);
}
- final int description = mAccessibilityService.isEnabled()
- ? R.string.screen_pinning_description_accessible
- : R.string.screen_pinning_description;
((TextView) mLayout.findViewById(R.id.screen_pinning_description))
- .setText(description);
+ .setText(R.string.screen_pinning_description);
final int backBgVisibility =
mAccessibilityService.isEnabled() ? View.INVISIBLE : View.VISIBLE;
mLayout.findViewById(R.id.screen_pinning_back_bg).setVisibility(backBgVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index ebd4f63..e7fca6e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -268,17 +268,6 @@
return null;
}
- /** Allow a task to resize. */
- public void setTaskResizeable(int taskId) {
- if (mIam == null) return;
-
- try {
- mIam.setTaskResizeable(taskId, true);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
/**
* Resizes the given task to the new bounds.
*/
@@ -292,8 +281,8 @@
}
}
- /** Docks a task to the side of the screen. */
- public void dockTask(int taskId, int createMode) {
+ /** Docks a task to the side of the screen and starts it. */
+ public void startTaskInDockedMode(int taskId, int createMode) {
if (mIam == null) return;
try {
@@ -303,6 +292,17 @@
}
}
+ /** Docks an already resumed task to the side of the screen. */
+ public void moveTaskToDockedStack(int taskId, int createMode) {
+ if (mIam == null) return;
+
+ try {
+ mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Returns the focused stack id. */
public int getFocusedStack() {
if (mIam == null) return -1;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index ad0d758..d5d0713 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -785,8 +785,7 @@
// Dock the new task if we are hovering over a valid dock state
if (event.dockState != TaskStack.DockState.NONE) {
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.setTaskResizeable(event.task.key.id);
- ssp.dockTask(event.task.key.id, event.dockState.createMode);
+ ssp.startTaskInDockedMode(event.task.key.id, event.dockState.createMode);
launchTask(event.task, null, INVALID_STACK_ID);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 8938669..2c16f81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -217,7 +217,7 @@
private boolean mDeviceProvisioned = false;
- private RecentsComponent mRecents;
+ protected RecentsComponent mRecents;
protected int mZenMode;
@@ -1275,20 +1275,22 @@
int maxHeight = mRowMaxHeight;
final StatusBarNotification sbn = entry.notification;
- RemoteViews contentView = sbn.getNotification().contentView;
- RemoteViews bigContentView = sbn.getNotification().bigContentView;
- RemoteViews headsUpContentView = sbn.getNotification().headsUpContentView;
+ entry.cacheContentViews(mContext, null);
+
+ final RemoteViews contentView = entry.cachedContentView;
+ final RemoteViews bigContentView = entry.cachedBigContentView;
+ final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
+ final RemoteViews publicContentView = entry.cachedPublicContentView;
if (contentView == null) {
+ Log.v(TAG, "no contentView for: " + sbn.getNotification());
return false;
}
if (DEBUG) {
- Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
+ Log.v(TAG, "publicContentView: " + publicContentView);
}
- Notification publicNotification = sbn.getNotification().publicVersion;
-
ExpandableNotificationRow row;
// Stash away previous user expansion state so we can restore it at
@@ -1377,9 +1379,9 @@
// now the public version
View publicViewLocal = null;
- if (publicNotification != null) {
+ if (publicContentView != null) {
try {
- publicViewLocal = publicNotification.contentView.apply(
+ publicViewLocal = publicContentView.apply(
sbn.getPackageContext(mContext),
contentContainerPublic, mOnClickHandler);
@@ -1537,30 +1539,9 @@
}
if (viableAction != null) {
- Notification stripped = n.clone();
- Notification.Builder.stripForDelivery(stripped);
- stripped.extras.putBoolean("android.rebuild", true);
- stripped.actions = new Notification.Action[] { viableAction };
- stripped.extras.putBoolean("android.rebuild.contentView", true);
- stripped.contentView = null;
- stripped.extras.putBoolean("android.rebuild.bigView", true);
- stripped.bigContentView = null;
- stripped.extras.putBoolean("android.rebuild.hudView", true);
- stripped.headsUpContentView = null;
-
- stripped.extras.putParcelable(Notification.EXTRA_LARGE_ICON,
- stripped.getLargeIcon());
- if (SystemProperties.getBoolean("debug.strip_third_line", false)) {
- stripped.extras.putCharSequence(Notification.EXTRA_INFO_TEXT, null);
- stripped.extras.putCharSequence(Notification.EXTRA_SUMMARY_TEXT, null);
- }
-
- Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);
-
- n.actions = rebuilt.actions;
- n.bigContentView = rebuilt.bigContentView;
- n.headsUpContentView = rebuilt.headsUpContentView;
- n.publicVersion = rebuilt.publicVersion;
+ Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
+ rebuilder.setActions(viableAction);
+ rebuilder.build(); // will rewrite n
}
}
}
@@ -2034,12 +2015,15 @@
}
Notification n = notification.getNotification();
- if (DEBUG) {
- logUpdate(entry, n);
- }
- boolean applyInPlace = shouldApplyInPlace(entry, n);
+
+ boolean applyInPlace = !entry.cacheContentViews(mContext, notification.getNotification());
boolean shouldInterrupt = shouldInterrupt(entry, notification);
boolean alertAgain = alertAgain(entry, n);
+ if (DEBUG) {
+ Log.d(TAG, "applyInPlace=" + applyInPlace
+ + " shouldInterrupt=" + shouldInterrupt
+ + " alertAgain=" + alertAgain);
+ }
entry.notification = notification;
mGroupManager.onEntryUpdated(entry, entry.notification);
@@ -2104,101 +2088,32 @@
protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldInterrupt,
boolean alertAgain);
- private void logUpdate(Entry oldEntry, Notification n) {
- StatusBarNotification oldNotification = oldEntry.notification;
- Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
- + " ongoing=" + oldNotification.isOngoing()
- + " expanded=" + oldEntry.getContentView()
- + " contentView=" + oldNotification.getNotification().contentView
- + " bigContentView=" + oldNotification.getNotification().bigContentView
- + " publicView=" + oldNotification.getNotification().publicVersion
- + " rowParent=" + oldEntry.row.getParent());
- Log.d(TAG, "new notification: when=" + n.when
- + " ongoing=" + oldNotification.isOngoing()
- + " contentView=" + n.contentView
- + " bigContentView=" + n.bigContentView
- + " publicView=" + n.publicVersion);
- }
-
- /**
- * @return whether we can just reapply the RemoteViews from a notification in-place when it is
- * updated
- */
- private boolean shouldApplyInPlace(Entry entry, Notification n) {
- StatusBarNotification oldNotification = entry.notification;
- // XXX: modify when we do something more intelligent with the two content views
- final RemoteViews oldContentView = oldNotification.getNotification().contentView;
- final RemoteViews contentView = n.contentView;
- final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
- final RemoteViews bigContentView = n.bigContentView;
- final RemoteViews oldHeadsUpContentView
- = oldNotification.getNotification().headsUpContentView;
- final RemoteViews headsUpContentView = n.headsUpContentView;
- final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
- final RemoteViews oldPublicContentView = oldPublicNotification != null
- ? oldPublicNotification.contentView : null;
- final Notification publicNotification = n.publicVersion;
- final RemoteViews publicContentView = publicNotification != null
- ? publicNotification.contentView : null;
- boolean contentsUnchanged = entry.getContentView() != null
- && contentView.getPackage() != null
- && oldContentView.getPackage() != null
- && oldContentView.getPackage().equals(contentView.getPackage())
- && oldContentView.getLayoutId() == contentView.getLayoutId();
- // large view may be null
- boolean bigContentsUnchanged =
- (entry.getExpandedContentView() == null && bigContentView == null)
- || ((entry.getExpandedContentView() != null && bigContentView != null)
- && bigContentView.getPackage() != null
- && oldBigContentView.getPackage() != null
- && oldBigContentView.getPackage().equals(bigContentView.getPackage())
- && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
- boolean headsUpContentsUnchanged =
- (oldHeadsUpContentView == null && headsUpContentView == null)
- || ((oldHeadsUpContentView != null && headsUpContentView != null)
- && headsUpContentView.getPackage() != null
- && oldHeadsUpContentView.getPackage() != null
- && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
- && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
- boolean publicUnchanged =
- (oldPublicContentView == null && publicContentView == null)
- || ((oldPublicContentView != null && publicContentView != null)
- && publicContentView.getPackage() != null
- && oldPublicContentView.getPackage() != null
- && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
- && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
- return contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
- && publicUnchanged;
- }
-
- private void updateNotificationViews(Entry entry, StatusBarNotification notification) {
- final RemoteViews contentView = notification.getNotification().contentView;
- final RemoteViews bigContentView = notification.getNotification().bigContentView;
- final RemoteViews headsUpContentView = notification.getNotification().headsUpContentView;
- final Notification publicVersion = notification.getNotification().publicVersion;
- final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
- : null;
+ private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
+ final RemoteViews contentView = entry.cachedContentView;
+ final RemoteViews bigContentView = entry.cachedBigContentView;
+ final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
+ final RemoteViews publicContentView = entry.cachedPublicContentView;
// Reapply the RemoteViews
contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
if (bigContentView != null && entry.getExpandedContentView() != null) {
- bigContentView.reapply(notification.getPackageContext(mContext),
+ bigContentView.reapply(sbn.getPackageContext(mContext),
entry.getExpandedContentView(),
mOnClickHandler);
}
View headsUpChild = entry.getHeadsUpContentView();
if (headsUpContentView != null && headsUpChild != null) {
- headsUpContentView.reapply(notification.getPackageContext(mContext),
+ headsUpContentView.reapply(sbn.getPackageContext(mContext),
headsUpChild, mOnClickHandler);
}
if (publicContentView != null && entry.getPublicContentView() != null) {
- publicContentView.reapply(notification.getPackageContext(mContext),
+ publicContentView.reapply(sbn.getPackageContext(mContext),
entry.getPublicContentView(), mOnClickHandler);
}
// update the contentIntent
- mNotificationClicker.register(entry.row, notification);
+ mNotificationClicker.register(entry.row, sbn);
- entry.row.setStatusBarNotification(notification);
+ entry.row.setStatusBarNotification(sbn);
entry.row.notifyContentUpdated();
entry.row.resetHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index aedae52..6a90d8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import android.app.Notification;
+import android.content.Context;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -24,6 +25,7 @@
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.view.View;
+import android.widget.RemoteViews;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -53,6 +55,10 @@
public boolean legacy; // whether the notification has a legacy, dark background
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
+ public RemoteViews cachedContentView;
+ public RemoteViews cachedBigContentView;
+ public RemoteViews cachedHeadsUpContentView;
+ public RemoteViews cachedPublicContentView;
public Entry(StatusBarNotification n, StatusBarIconView ic) {
this.key = n.getKey();
@@ -98,6 +104,67 @@
return row.getPublicLayout().getContractedChild();
}
+ public boolean cacheContentViews(Context ctx, Notification updatedNotification) {
+ boolean cached = false;
+ if (updatedNotification != null) {
+ final Notification.Builder updatedNotificationBuilder
+ = Notification.Builder.recoverBuilder(ctx, updatedNotification);
+ final RemoteViews newContentView = updatedNotificationBuilder.makeContentView();
+ if (!compareRemoteViews(cachedContentView, newContentView)) {
+ cachedContentView = newContentView;
+ cached |= true;
+ }
+ final RemoteViews newBigContentView =
+ updatedNotificationBuilder.makeBigContentView();
+ if (!compareRemoteViews(cachedBigContentView, newBigContentView)) {
+ cachedBigContentView = newBigContentView;
+ cached |= true;
+ }
+ final RemoteViews newHeadsUpContentView =
+ updatedNotificationBuilder.makeHeadsUpContentView();
+ if (!compareRemoteViews(cachedHeadsUpContentView, newBigContentView)) {
+ cachedHeadsUpContentView = newHeadsUpContentView;
+ cached |= true;
+ }
+ final Notification updatedPublicNotification = updatedNotification.publicVersion;
+ final RemoteViews newPubContentView = (updatedPublicNotification != null)
+ ? Notification.Builder.recoverBuilder(
+ ctx, updatedPublicNotification).makeContentView()
+ : null;
+ if (!compareRemoteViews(cachedPublicContentView, newPubContentView)) {
+ cachedPublicContentView = newPubContentView;
+ cached |= true;
+ }
+ } else {
+ final Notification.Builder builder
+ = Notification.Builder.recoverBuilder(ctx, notification.getNotification());
+
+ cachedContentView = builder.makeContentView();
+ cachedBigContentView = builder.makeBigContentView();
+ cachedHeadsUpContentView = builder.makeHeadsUpContentView();
+
+ final Notification publicNotification =
+ notification.getNotification().publicVersion;
+ if (publicNotification != null) {
+ final Notification.Builder publicBuilder
+ = Notification.Builder.recoverBuilder(ctx, publicNotification);
+ cachedPublicContentView = publicBuilder.makeContentView();
+ }
+ cached = true;
+ }
+ return cached;
+ }
+
+ // Returns true if the RemoteViews are the same.
+ private boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
+ return (a == null && b == null) ||
+ (a != null && b != null
+ && b.getPackage() != null
+ && a.getPackage() != null
+ && a.getPackage().equals(b.getPackage())
+ && a.getLayoutId() == b.getLayoutId());
+ }
+
public void notifyFullScreenIntentLaunched() {
lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index cfde791..cfdb01e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -88,7 +88,6 @@
import android.view.ViewStub;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
@@ -146,7 +145,6 @@
import com.android.systemui.statusbar.policy.FullscreenUserSwitcher;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
@@ -1115,13 +1113,22 @@
}
};
- private long mLastLockToAppLongPress;
- private View.OnLongClickListener mLongPressBackRecentsListener =
- new View.OnLongClickListener() {
+ private View.OnLongClickListener mLongPressBackListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- handleLongPressBackRecents(v);
- return true;
+ return handleLongPressBack();
+ }
+ };
+
+ private View.OnLongClickListener mRecentsLongClickListener = new View.OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (mRecents != null) {
+ mRecents.dockTopTask();
+ return true;
+ }
+ return false;
}
};
@@ -1170,9 +1177,9 @@
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
mNavigationBarView.getRecentsButton().setLongClickable(true);
- mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
+ mNavigationBarView.getRecentsButton().setOnLongClickListener(mRecentsLongClickListener);
mNavigationBarView.getBackButton().setLongClickable(true);
- mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
+ mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
mNavigationBarView.getHomeButton().setOnLongClickListener(mLongPressHomeListener);
mAssistManager.onConfigurationChanged();
@@ -4048,7 +4055,7 @@
private void vibrateForCameraGesture() {
// Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
- mVibrator.vibrate(new long[] { 0, 750L }, -1 /* repeat */);
+ mVibrator.vibrate(new long[]{0, 750L}, -1 /* repeat */);
}
public void onScreenTurnedOn() {
@@ -4057,58 +4064,22 @@
}
/**
- * This handles long-press of both back and recents. They are
- * handled together to capture them both being long-pressed
- * at the same time to exit screen pinning (lock task).
- *
- * When accessibility mode is on, only a long-press from recents
- * is required to exit.
- *
- * In all other circumstances we try to pass through long-press events
- * for Back, so that apps can still use it. Which can be from two things.
- * 1) Not currently in screen pinning (lock task).
- * 2) Back is long-pressed without recents.
+ * Handles long press for back button. This exits screen pinning.
*/
- private void handleLongPressBackRecents(View v) {
+ private boolean handleLongPressBack() {
try {
- boolean sendBackLongPress = false;
IActivityManager activityManager = ActivityManagerNative.getDefault();
- boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
- if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
- long time = System.currentTimeMillis();
- // If we recently long-pressed the other button then they were
- // long-pressed 'together'
- if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
- activityManager.stopLockTaskModeOnCurrent();
- // When exiting refresh disabled flags.
- mNavigationBarView.setDisabledFlags(mDisabled1, true);
- } else if ((v.getId() == R.id.back)
- && !mNavigationBarView.getRecentsButton().isPressed()) {
- // If we aren't pressing recents right now then they presses
- // won't be together, so send the standard long-press action.
- sendBackLongPress = true;
- }
- mLastLockToAppLongPress = time;
- } else {
- // If this is back still need to handle sending the long-press event.
- if (v.getId() == R.id.back) {
- sendBackLongPress = true;
- } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
- // When in accessibility mode a long press that is recents (not back)
- // should stop lock task.
- activityManager.stopLockTaskModeOnCurrent();
- // When exiting refresh disabled flags.
- mNavigationBarView.setDisabledFlags(mDisabled1, true);
- }
- }
- if (sendBackLongPress) {
- KeyButtonView keyButtonView = (KeyButtonView) v;
- keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
- keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+ if (activityManager.isInLockTaskMode()) {
+ activityManager.stopLockTaskModeOnCurrent();
+
+ // When exiting refresh disabled flags.
+ mNavigationBarView.setDisabledFlags(mDisabled1, true);
+ return true;
}
} catch (RemoteException e) {
Log.d(TAG, "Unable to reach activity manager", e);
}
+ return false;
}
// Recents
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 496e4df..927b995 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1500,7 +1500,7 @@
mState = STATE_SENSING;
if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
EventLogTags.writeDeviceIdle(mState, "step");
- cancelAlarmLocked();
+ scheduleAlarmLocked(mConstants.SENSING_TIMEOUT, false);
cancelLocatingLocked();
mAnyMotionDetector.checkForAnyMotion();
mNotMoving = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c728b39..257f034 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;
@@ -74,6 +75,7 @@
import android.os.storage.IMountService;
import android.os.storage.MountServiceInternal;
import android.os.storage.StorageManager;
+import android.provider.Settings.Global;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionSession;
import android.util.ArrayMap;
@@ -1188,6 +1190,7 @@
String mOrigDebugApp = null;
boolean mOrigWaitForDebugger = false;
boolean mAlwaysFinishActivities = false;
+ boolean mForceResizableActivites;
IActivityController mController = null;
String mProfileApp = null;
ProcessRecord mProfileProc = null;
@@ -8638,9 +8641,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 +9085,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,
@@ -11613,14 +11640,17 @@
private void retrieveSettings() {
final ContentResolver resolver = mContext.getContentResolver();
- String debugApp = Settings.Global.getString(
- resolver, Settings.Global.DEBUG_APP);
+ String debugApp = Settings.Global.getString(resolver, Settings.Global.DEBUG_APP);
boolean waitForDebugger = Settings.Global.getInt(
resolver, Settings.Global.WAIT_FOR_DEBUGGER, 0) != 0;
boolean alwaysFinishActivities = Settings.Global.getInt(
resolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) != 0;
boolean forceRtl = Settings.Global.getInt(
resolver, Settings.Global.DEVELOPMENT_FORCE_RTL, 0) != 0;
+ int defaultForceResizable = Build.IS_DEBUGGABLE ? 1 : 0;
+ boolean forceResizable = Settings.Global.getInt(
+ resolver, Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
+ defaultForceResizable) != 0;
// Transfer any global setting for forcing RTL layout, into a System Property
SystemProperties.set(Settings.Global.DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
@@ -11635,6 +11665,7 @@
mDebugApp = mOrigDebugApp = debugApp;
mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger;
mAlwaysFinishActivities = alwaysFinishActivities;
+ mForceResizableActivites = forceResizable;
// This happens before any activities are started, so we can
// change mConfiguration in-place.
updateConfigurationLocked(configuration, null, true);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index fd4da0c..1cd3758 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -23,6 +23,7 @@
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.app.ActivityManager.INVALID_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.*;
@@ -254,6 +255,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;
@@ -355,10 +359,6 @@
return count;
}
- int numTasks() {
- return mTaskHistory.size();
- }
-
ActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
RecentTasks recentTasks) {
mActivityContainer = activityContainer;
@@ -371,6 +371,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) {
@@ -1307,6 +1308,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) {
@@ -1329,8 +1335,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;
}
@@ -1344,9 +1351,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;
}
}
@@ -2785,9 +2793,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 {
@@ -4635,6 +4644,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 99f7ec6..cd2f124 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -295,10 +295,6 @@
/** Set when we have taken too long waiting to go to sleep. */
boolean mSleepTimeout = false;
- /** Indicates if we are running on a Leanback-only (TV) device. Only initialized after
- * setWindowManager is called. **/
- private boolean mLeanbackOnlyDevice;
-
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
@@ -446,9 +442,6 @@
mHomeStack = mFocusedStack = mLastFocusedStack = getStack(HOME_STACK_ID);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
-
- // Initialize this here, now that we can get a valid reference to PackageManager.
- mLeanbackOnlyDevice = isLeanbackOnlyDevice();
}
}
@@ -1780,70 +1773,68 @@
ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds) {
final TaskRecord task = r.task;
- // On leanback only devices we should keep all activities in the same stack.
- if (!mLeanbackOnlyDevice &&
- (r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
+ if (!(r.isApplicationActivity() || (task != null && task.isApplicationTask()))) {
+ return mHomeStack;
+ }
- ActivityStack stack;
+ ActivityStack stack;
- if (task != null && task.stack != null) {
- stack = task.stack;
- if (stack.isOnHomeDisplay()) {
- if (mFocusedStack != stack) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Setting " + "focused stack to r=" + r
- + " task=" + task);
- } else {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Focused stack already=" + mFocusedStack);
- }
- }
- return stack;
- }
-
- final ActivityContainer container = r.mInitialActivityContainer;
- if (container != null) {
- // The first time put it on the desired stack, after this put on task stack.
- r.mInitialActivityContainer = null;
- return container.mStack;
- }
-
- // The fullscreen stack can contain any task regardless of if the task is resizeable
- // or not. So, we let the task go in the fullscreen task if it is the focus stack.
- // If the freeform stack has focus, and the activity to be launched is resizeable,
- // we can also put it in the focused stack.
- final boolean canUseFocusedStack =
- mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
- || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
- if (canUseFocusedStack
- && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Have a focused stack=" + mFocusedStack);
- return mFocusedStack;
- }
-
- // We first try to put the task in the first dynamic stack.
- final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
- for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- stack = homeDisplayStacks.get(stackNdx);
- final boolean isDynamicStack = stack.mStackId >= FIRST_DYNAMIC_STACK_ID;
- if (isDynamicStack) {
+ if (task != null && task.stack != null) {
+ stack = task.stack;
+ if (stack.isOnHomeDisplay()) {
+ if (mFocusedStack != stack) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
- "computeStackFocus: Setting focused stack=" + stack);
- return stack;
+ "computeStackFocus: Setting " + "focused stack to r=" + r
+ + " task=" + task);
+ } else {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Focused stack already=" + mFocusedStack);
}
}
-
- // If there is no suitable dynamic stack then we figure out which static stack to use.
- final int stackId = task != null ? task.getLaunchStackId() :
- bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
- FULLSCREEN_WORKSPACE_STACK_ID;
- stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
- if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
- + r + " stackId=" + stack.mStackId);
return stack;
}
- return mHomeStack;
+
+ final ActivityContainer container = r.mInitialActivityContainer;
+ if (container != null) {
+ // The first time put it on the desired stack, after this put on task stack.
+ r.mInitialActivityContainer = null;
+ return container.mStack;
+ }
+
+ // The fullscreen stack can contain any task regardless of if the task is resizeable
+ // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+ // If the freeform stack has focus, and the activity to be launched is resizeable,
+ // we can also put it in the focused stack.
+ final boolean canUseFocusedStack =
+ mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
+ if (canUseFocusedStack
+ && (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Have a focused stack=" + mFocusedStack);
+ return mFocusedStack;
+ }
+
+ // We first try to put the task in the first dynamic stack.
+ final ArrayList<ActivityStack> homeDisplayStacks = mHomeStack.mStacks;
+ for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ stack = homeDisplayStacks.get(stackNdx);
+ final boolean isDynamicStack = stack.mStackId >= FIRST_DYNAMIC_STACK_ID;
+ if (isDynamicStack) {
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
+ "computeStackFocus: Setting focused stack=" + stack);
+ return stack;
+ }
+ }
+
+ // If there is no suitable dynamic stack then we figure out which static stack to use.
+ final int stackId = task != null ? task.getLaunchStackId() :
+ bounds != null ? FREEFORM_WORKSPACE_STACK_ID :
+ FULLSCREEN_WORKSPACE_STACK_ID;
+ stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
+ if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
+ + r + " stackId=" + stack.mStackId);
+ return stack;
}
boolean setFocusedStack(ActivityRecord r, String reason) {
@@ -2758,8 +2749,7 @@
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
- final int numStacks = stacks.size();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack.finishDisabledPackageActivitiesLocked(
packageName, filterByClasses, doit, evenPersistent, userId)) {
@@ -2885,13 +2875,16 @@
}
if (stackId != task.stack.mStackId) {
- moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, FORCE_FOCUS, reason);
- } else {
- task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
- task.getTopActivity() == null ? null : task.getTopActivity().appTimeTracker,
- reason);
+ moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, reason);
+
+ // moveTaskToStackUncheckedLocked() should already placed the task on top,
+ // still need moveTaskToFrontLocked() below for any transition settings.
}
+ final ActivityRecord r = task.getTopActivity();
+ task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options,
+ r == null ? null : r.appTimeTracker, reason);
+
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
}
@@ -2998,84 +2991,87 @@
}
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
+ mWindowManager.deferSurfaceLayout();
+ try {
+ ActivityRecord r = stack.topRunningActivityLocked();
- ActivityRecord r = stack.topRunningActivityLocked();
-
- mTmpBounds.clear();
- mTmpConfigs.clear();
- ArrayList<TaskRecord> tasks = stack.getAllTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- TaskRecord task = tasks.get(i);
- if (task.mResizeable) {
- if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
- // For freeform stack we don't adjust the size of the tasks to match that
- // of the stack, but we do try to make sure the tasks are still contained
- // with the bounds of the stack.
- tempRect2.set(task.mBounds);
- fitWithinBounds(tempRect2, bounds);
- task.updateOverrideConfiguration(tempRect2);
- } else {
- task.updateOverrideConfiguration(bounds);
- }
- }
-
- mTmpConfigs.put(task.taskId, task.mOverrideConfig);
- mTmpBounds.put(task.taskId, task.mBounds);
- }
- stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
- if (stack.mStackId == DOCKED_STACK_ID) {
- // Dock stack funness...Yay!
- if (stack.mFullscreen) {
- // The dock stack went fullscreen which is kinda like dismissing it.
- // In this case we make all other static stacks fullscreen and move all
- // docked stack tasks to the fullscreen stack.
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i != DOCKED_STACK_ID && getStack(i) != null) {
- resizeStackLocked(i, null, preserveWindows, true);
+ mTmpBounds.clear();
+ mTmpConfigs.clear();
+ ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ TaskRecord task = tasks.get(i);
+ if (task.mResizeable) {
+ if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ // For freeform stack we don't adjust the size of the tasks to match that
+ // of the stack, but we do try to make sure the tasks are still contained
+ // with the bounds of the stack.
+ tempRect2.set(task.mBounds);
+ fitWithinBounds(tempRect2, bounds);
+ task.updateOverrideConfiguration(tempRect2);
+ } else {
+ task.updateOverrideConfiguration(bounds);
}
}
- final int count = tasks.size();
- for (int i = 0; i < count; i++) {
- moveTaskToStackLocked(tasks.get(i).taskId,
- FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS);
- }
+ mTmpConfigs.put(task.taskId, task.mOverrideConfig);
+ mTmpBounds.put(task.taskId, task.mBounds);
+ }
+ stack.mFullscreen = mWindowManager.resizeStack(stackId, bounds, mTmpConfigs, mTmpBounds);
+ if (stack.mStackId == DOCKED_STACK_ID) {
+ // Dock stack funness...Yay!
+ if (stack.mFullscreen) {
+ // The dock stack went fullscreen which is kinda like dismissing it.
+ // In this case we make all other static stacks fullscreen and move all
+ // docked stack tasks to the fullscreen stack.
+ for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+ if (i != DOCKED_STACK_ID && getStack(i) != null) {
+ resizeStackLocked(i, null, preserveWindows, true);
+ }
+ }
- // stack shouldn't contain anymore activities, so nothing to resume.
- r = null;
- } else {
- // Docked stacks occupy a dedicated region on screen so the size of all other
- // static stacks need to be adjusted so they don't overlap with the docked stack.
- // We get the bounds to use from window manager which has been adjusted for any
- // screen controls and is also the same for all stacks.
- mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect);
+ final int count = tasks.size();
+ for (int i = 0; i < count; i++) {
+ moveTaskToStackLocked(tasks.get(i).taskId,
+ FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, FORCE_FOCUS);
+ }
- for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i != DOCKED_STACK_ID) {
- ActivityStack otherStack = getStack(i);
- if (otherStack != null) {
- resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true);
+ // stack shouldn't contain anymore activities, so nothing to resume.
+ r = null;
+ } else {
+ // Docked stacks occupy a dedicated region on screen so the size of all other
+ // static stacks need to be adjusted so they don't overlap with the docked stack.
+ // We get the bounds to use from window manager which has been adjusted for any
+ // screen controls and is also the same for all stacks.
+ mWindowManager.getStackDockedModeBounds(HOME_STACK_ID, tempRect);
+
+ for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
+ if (i != DOCKED_STACK_ID) {
+ ActivityStack otherStack = getStack(i);
+ if (otherStack != null) {
+ resizeStackLocked(i, tempRect, PRESERVE_WINDOWS, true);
+ }
}
}
}
+ // Since we are resizing the stack, all other operations should strive to preserve
+ // windows.
+ preserveWindows = true;
}
- // Since we are resizing the stack, all other operations should strive to preserve
- // windows.
- preserveWindows = true;
- }
- stack.setBounds(bounds);
+ stack.setBounds(bounds);
- if (r != null) {
- final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
- // And we need to make sure at this point that all other activities
- // are made visible with the correct configuration.
- ensureActivitiesVisibleLocked(r, 0, preserveWindows);
- if (!updated) {
- resumeTopActivitiesLocked(stack, null, null);
+ if (r != null) {
+ final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, preserveWindows);
+ // And we need to make sure at this point that all other activities
+ // are made visible with the correct configuration.
+ ensureActivitiesVisibleLocked(r, 0, preserveWindows);
+ if (!updated) {
+ resumeTopActivitiesLocked(stack, null, null);
+ }
}
+ } finally {
+ mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
-
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
@@ -3198,7 +3194,7 @@
*/
private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
if (stackId == INVALID_STACK_ID) {
- stackId = mLeanbackOnlyDevice ? mHomeStack.mStackId : task.getLaunchStackId();
+ stackId = task.getLaunchStackId();
}
if (task.stack != null) {
// Task has already been restored once. See if we need to do anything more
@@ -3265,23 +3261,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;
}
@@ -3293,7 +3274,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
@@ -3314,7 +3296,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);
}
@@ -3325,6 +3307,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) {
@@ -4924,18 +4946,6 @@
}
}
- private boolean isLeanbackOnlyDevice() {
- boolean onLeanbackOnly = false;
- try {
- onLeanbackOnly = AppGlobals.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_LEANBACK_ONLY);
- } catch (RemoteException e) {
- // noop
- }
-
- return onLeanbackOnly;
- }
-
/**
* Adjust bounds to stay within stack bounds.
*
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index fe87a93..120b40c 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;
@@ -317,7 +318,7 @@
mNextAffiliateTaskId = nextTaskId;
mCallingUid = callingUid;
mCallingPackage = callingPackage;
- mResizeable = resizeable;
+ mResizeable = resizeable || mService.mForceResizableActivites;
mPrivileged = privileged;
ActivityInfo info = mActivities.get(0).info;
mMinimalSize = info != null && info.layout != null ? info.layout.minimalSize : -1;
@@ -419,7 +420,7 @@
} else {
autoRemoveRecents = false;
}
- mResizeable = info.resizeable;
+ mResizeable = info.resizeable || mService.mForceResizableActivites;
mLockTaskMode = info.lockTaskLaunchMode;
mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0;
setLockTaskAuth();
@@ -625,6 +626,9 @@
// Only set this based on the first activity
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
+ if (taskType == HOME_ACTIVITY_TYPE && mService.mForceResizableActivites) {
+ mResizeable = r.info.resizeable;
+ }
isPersistable = r.isPersistable();
mCallingUid = r.launchedFromUid;
mCallingPackage = r.launchedFromPackage;
@@ -1189,14 +1193,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 +1239,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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e8e46ef..361bbf9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2146,13 +2146,6 @@
+ " id=" + id + " notification=" + notification);
}
- if (notification.getSmallIcon() != null) {
- if (!notification.isValid()) {
- throw new IllegalArgumentException("Invalid notification (): pkg=" + pkg
- + " id=" + id + " notification=" + notification);
- }
- }
-
mHandler.post(new Runnable() {
@Override
public void run() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b3e7f5f..aed2f0b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2276,7 +2276,7 @@
mSettings.enableSystemPackageLPw(packageName);
try {
- scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, UserHandle.SYSTEM);
+ scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
@@ -5685,7 +5685,7 @@
}
try {
scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
- scanFlags, currentTime, UserHandle.SYSTEM);
+ scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
@@ -5791,8 +5791,6 @@
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
- Preconditions.checkNotNull(user);
-
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();
@@ -12141,7 +12139,7 @@
int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
try {
scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
- UserHandle.SYSTEM);
+ null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
+ e.getMessage());
@@ -13276,8 +13274,7 @@
final PackageParser.Package newPkg;
try {
- newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0,
- UserHandle.SYSTEM);
+ newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + newPs.name + ": " + e.getMessage());
return false;
@@ -15841,8 +15838,7 @@
synchronized (mInstallLock) {
PackageParser.Package pkg = null;
try {
- pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0,
- UserHandle.SYSTEM);
+ pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage());
}
@@ -16005,8 +16001,7 @@
synchronized (mInstallLock) {
final PackageParser.Package pkg;
try {
- pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0,
- UserHandle.SYSTEM);
+ pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
loaded.add(pkg.applicationInfo);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 2365a45..dc34904 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -456,10 +456,12 @@
return -startPos / denom;
}
- private Animation createScaleUpAnimationLocked(
- int transit, boolean enter, int appWidth, int appHeight) {
- Animation a = null;
+ private Animation createScaleUpAnimationLocked(int transit, boolean enter,
+ Rect containingFrame) {
+ Animation a;
getDefaultNextAppTransitionStartRect(mTmpStartRect);
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
if (enter) {
// Entering app zooms out from the center of the initial rect.
float scaleW = mTmpStartRect.width() / (float) appWidth;
@@ -746,10 +748,11 @@
* activity that is leaving, and the activity that is entering.
*/
Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
- int appWidth, int appHeight, int orientation, int transit, Rect containingFrame,
- Rect contentInsets, @Nullable Rect surfaceInsets, boolean resizedWindow,
- int taskId) {
+ int orientation, int transit, Rect containingFrame, Rect contentInsets,
+ @Nullable Rect surfaceInsets, boolean freeform, int taskId) {
Animation a;
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
getDefaultNextAppTransitionStartRect(mTmpStartRect);
final int thumbWidthI = mTmpStartRect.width();
final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -762,7 +765,7 @@
switch (thumbTransitState) {
case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
- if (resizedWindow) {
+ if (freeform) {
a = createAspectScaledThumbnailEnterNonFullscreenAnimationLocked(
containingFrame, surfaceInsets, taskId);
} else {
@@ -953,8 +956,10 @@
* This animation is created when we are doing a thumbnail transition, for the activity that is
* leaving, and the activity that is entering.
*/
- Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
- int appHeight, int transit, int taskId) {
+ Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, Rect containingFrame,
+ int transit, int taskId) {
+ final int appWidth = containingFrame.width();
+ final int appHeight = containingFrame.height();
Bitmap thumbnailHeader = getAppTransitionThumbnailHeader(taskId);
Animation a;
getDefaultNextAppTransitionStartRect(mTmpStartRect);
@@ -1016,18 +1021,22 @@
return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
}
- private Animation createRelaunchAnimation(int appWidth, int appHeight,
- Rect containingFrame) {
+ private Animation createRelaunchAnimation(Rect containingFrame, Rect contentInsets) {
getDefaultNextAppTransitionStartRect(mTmpFromClipRect);
final int left = mTmpFromClipRect.left;
final int top = mTmpFromClipRect.top;
mTmpFromClipRect.offset(-left, -top);
- mTmpToClipRect.set(0, 0, appWidth, appHeight);
+ // TODO: Isn't that strange that we ignore exact position of the containingFrame?
+ mTmpToClipRect.set(0, 0, containingFrame.width(), containingFrame.height());
AnimationSet set = new AnimationSet(true);
float fromWidth = mTmpFromClipRect.width();
float toWidth = mTmpToClipRect.width();
float fromHeight = mTmpFromClipRect.height();
- float toHeight = mTmpToClipRect.height();
+ // While the window might span the whole display, the actual content will be cropped to the
+ // system decoration frame, for example when the window is docked. We need to take into
+ // account the visible height when constructing the animation.
+ float toHeight = mTmpToClipRect.height() - contentInsets.top - contentInsets.bottom;
+ int translateAdjustment = 0;
if (fromWidth <= toWidth && fromHeight <= toHeight) {
// The final window is larger in both dimensions than current window (e.g. we are
// maximizing), so we can simply unclip the new window and there will be no disappearing
@@ -1037,12 +1046,17 @@
// The disappearing window has one larger dimension. We need to apply scaling, so the
// first frame of the entry animation matches the old window.
set.addAnimation(new ScaleAnimation(fromWidth / toWidth, 1, fromHeight / toHeight, 1));
+ // We might not be going exactly full screen, but instead be aligned under the status
+ // bar using cropping. We still need to account for the cropped part, which will also
+ // be scaled.
+ translateAdjustment = (int) (contentInsets.top * fromHeight / toHeight);
}
- // We might not be going exactly full screen, but instead be aligned under the status bar.
- // We need to take this into account when creating the translate animation.
+ // We animate the translation from the old position of the removed window, to the new
+ // position of the added window. The latter might not be full screen, for example docked for
+ // docked windows.
TranslateAnimation translate = new TranslateAnimation(left - containingFrame.left,
- 0, top - containingFrame.top, 0);
+ 0, top - containingFrame.top - translateAdjustment, 0);
set.addAnimation(translate);
set.setDuration(DEFAULT_APP_TRANSITION_DURATION);
set.setZAdjustment(Animation.ZORDER_TOP);
@@ -1060,10 +1074,30 @@
&& mNextAppTransitionType != NEXT_TRANSIT_TYPE_CLIP_REVEAL;
}
+ /**
+ *
+ * @param frame These are the bounds of the window when it finishes the animation. This is where
+ * the animation must usually finish in entrance animation, as the next frame will
+ * display the window at these coordinates. In case of exit animation, this is
+ * where the animation must start, as the frame before the animation is displaying
+ * the window at these bounds.
+ * @param insets Knowing where the window will be positioned is not enough. Some parts of the
+ * window might be obscured, usually by the system windows (status bar and
+ * navigation bar) and we use content insets to convey that information. This
+ * usually affects the animation aspects vertically, as the system decoration is
+ * at the top and the bottom. For example when we animate from full screen to
+ * recents, we want to exclude the covered parts, because they won't match the
+ * thumbnail after the last frame is executed.
+ * @param surfaceInsets In rare situation the surface is larger than the content and we need to
+ * know about this to make the animation frames match. We currently use
+ * this for freeform windows, which have larger surfaces to display
+ * shadows. When we animate them from recents, we want to match the content
+ * to the recents thumbnail and hence need to account for the surface being
+ * bigger.
+ */
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets,
- @Nullable Rect surfaceInsets, Rect appFrame, boolean isVoiceInteraction,
- boolean resizedWindow, int taskId) {
+ int orientation, Rect frame, Rect insets, @Nullable Rect surfaceInsets,
+ boolean isVoiceInteraction, boolean freeform, int taskId) {
Animation a;
if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN
|| transit == TRANSIT_TASK_OPEN
@@ -1086,7 +1120,7 @@
+ " anim=" + a + " transit=" + appTransitionToString(transit)
+ " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
} else if (transit == TRANSIT_ACTIVITY_RELAUNCH) {
- a = createRelaunchAnimation(appWidth, appHeight, containingFrame);
+ a = createRelaunchAnimation(frame, insets);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=" + mNextAppTransition
@@ -1108,14 +1142,14 @@
+ " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
- a = createClipRevealAnimationLocked(transit, enter, appFrame);
+ a = createClipRevealAnimationLocked(transit, enter, frame);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL"
+ " transit=" + appTransitionToString(transit)
+ " Callers=" + Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
- a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight);
+ a = createScaleUpAnimationLocked(transit, enter, frame);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
"applyAnimation:"
+ " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
@@ -1126,7 +1160,7 @@
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
- appWidth, appHeight, transit, taskId);
+ frame, transit, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
@@ -1140,8 +1174,8 @@
mNextAppTransitionScaleUp =
(mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
a = createAspectScaledThumbnailEnterExitAnimationLocked(
- getThumbnailTransitionState(enter), appWidth, appHeight, orientation, transit,
- containingFrame, contentInsets, surfaceInsets, resizedWindow, taskId);
+ getThumbnailTransitionState(enter), orientation, transit, frame,
+ insets, surfaceInsets, freeform, taskId);
if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
String animName = mNextAppTransitionScaleUp ?
"ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN";
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index bf7063f..a61e83f 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -300,7 +300,8 @@
return false;
}
- if ((mAppToken.allDrawn || animating || mAppToken.startingDisplayed)
+ if ((mAppToken.allDrawn || mAppToken.mAnimatingWithSavedSurface
+ || animating || mAppToken.startingDisplayed)
&& animation != null) {
if (!animating) {
if (DEBUG_ANIM) Slog.v(TAG,
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 5a1ed58..943e3ea 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -52,6 +52,13 @@
final boolean voiceInteraction;
+ // Whether the window has a saved surface from last pause, which can be
+ // used to start an entering animation earlier.
+ boolean mHasSavedSurface;
+
+ // Whether we're performing an entering animation with a saved surface.
+ boolean mAnimatingWithSavedSurface;
+
Task mTask;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -114,11 +121,14 @@
// This application will have its window replaced due to relaunch. This allows window manager
// to differentiate between simple removal of a window and replacement. In the latter case it
// will preserve the old window until the new one is drawn.
- boolean mReplacingWindow;
+ boolean mWillReplaceWindow;
// If true, the replaced window was already requested to be removed.
boolean mReplacingRemoveRequested;
// Whether the replacement of the window should trigger app transition animation.
boolean mAnimateReplacingWindow;
+ // If not null, the window that will be used to replace the old one. This is being set when
+ // the window is added and unset when this window reports its first draw.
+ WindowState mReplacingWindow;
AppWindowToken(WindowManagerService _service, IApplicationToken _token,
boolean _voiceInteraction) {
@@ -257,10 +267,13 @@
final int N = allAppWindows.size();
for (int i=0; i<N; i++) {
WindowState win = allAppWindows.get(i);
+ // If we're animating with a saved surface, we're already visible.
+ // Return true so that the alpha doesn't get cleared.
if (!win.mAppFreezing
- && (win.mViewVisibility == View.VISIBLE ||
- (win.mWinAnimator.isAnimating() &&
- !service.mAppTransition.isTransitionSet()))
+ && (win.mViewVisibility == View.VISIBLE
+ || mAnimatingWithSavedSurface
+ || (win.mWinAnimator.isAnimating() &&
+ !service.mAppTransition.isTransitionSet()))
&& !win.mDestroying && win.isDrawnLw()) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index f5e97e5..f35ea66 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -77,7 +77,6 @@
mSurfaceControl = surface;
mFlags = flags;
mLocalWin = localWin;
- mUid = Binder.getCallingUid();
mNotifiedWindows = new ArrayList<WindowState>();
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7c56180..4d11452 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,12 +17,15 @@
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.HOME_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 +190,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 +245,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;
@@ -322,6 +325,10 @@
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
}
+ boolean inHomeStack() {
+ return mStack != null && mStack.mStackId == HOME_STACK_ID;
+ }
+
boolean inFreeformWorkspace() {
return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
}
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(
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 928a117..f4a4140 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -31,6 +31,7 @@
import android.content.Context;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -413,7 +414,7 @@
final AppWindowToken atoken = win.mAppToken;
if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
- if (atoken == null || atoken.allDrawn) {
+ if (atoken == null || atoken.allDrawn || atoken.mAnimatingWithSavedSurface) {
if (winAnimator.performShowLocked()) {
setPendingLayoutChanges(displayId,
WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
@@ -731,8 +732,15 @@
mWindowPlacerLocked.requestTraversal();
}
+ if (mAnimating && !wasAnimating && Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
+
if (!mAnimating && wasAnimating) {
mWindowPlacerLocked.requestTraversal();
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
+ }
}
mService.destroyPreservedSurfaceLocked();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cd5fbb0..62553cf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1327,8 +1327,14 @@
addAttachedWindowToListLocked(win, addToToken);
}
- if (win.mAppToken != null && addToToken) {
- win.mAppToken.allAppWindows.add(win);
+ final AppWindowToken appToken = win.mAppToken;
+ if (appToken != null) {
+ if (addToToken) {
+ appToken.allAppWindows.add(win);
+ }
+ if (appToken.mWillReplaceWindow) {
+ appToken.mReplacingWindow = win;
+ }
}
}
@@ -2053,7 +2059,7 @@
}
private void prepareWindowReplacementTransition(AppWindowToken atoken) {
- if (atoken == null || !atoken.mReplacingWindow || !atoken.mAnimateReplacingWindow) {
+ if (atoken == null || !atoken.mWillReplaceWindow || !atoken.mAnimateReplacingWindow) {
return;
}
atoken.allDrawn = false;
@@ -2157,6 +2163,8 @@
+ " isAnimating=" + win.mWinAnimator.isAnimating()
+ " app-animation="
+ (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
+ + " mWillReplaceWindow="
+ + (win.mAppToken != null ? win.mAppToken.mWillReplaceWindow : false)
+ " inPendingTransaction="
+ (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
+ " mDisplayFrozen=" + mDisplayFrozen);
@@ -2168,8 +2176,8 @@
// animation wouldn't be seen.
if (win.mHasSurface && okToDisplay()) {
final AppWindowToken appToken = win.mAppToken;
- if (appToken != null && appToken.mReplacingWindow) {
- // This window is going to be replaced. We need to kepp it around until the new one
+ if (appToken != null && appToken.mWillReplaceWindow) {
+ // This window is going to be replaced. We need to keep it around until the new one
// gets added, then we will get rid of this one.
if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Preserving " + win + " until the new one is "
+ "added");
@@ -2679,9 +2687,16 @@
if (winAnimator.mSurfaceControl != null) {
if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
+ ": mExiting=" + win.mExiting);
+ // If we are using a saved surface to do enter animation, just let the
+ // animation run and don't destroy the surface. This could happen when
+ // the app sets visibility to invisible for the first time after resume,
+ // or when the user exits immediately after a resume. In both cases, we
+ // don't want to destroy the saved surface.
+ final boolean isAnimatingWithSavedSurface =
+ win.mAppToken != null && win.mAppToken.mAnimatingWithSavedSurface;
// If we are not currently running the exit animation, we
// need to see about starting one.
- if (!win.mExiting) {
+ if (!win.mExiting && !isAnimatingWithSavedSurface) {
surfaceChanged = true;
// Try starting an animation; if there isn't one, we
// can destroy the surface right away.
@@ -2833,12 +2848,19 @@
try {
synchronized (mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
- if (DEBUG_ADD_REMOVE) Slog.d(TAG, "finishDrawingWindow: " + win);
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "finishDrawingWindow: " + win + " mDrawState="
+ + (win != null ? win.mWinAnimator.drawStateToString() : "null"));
if (win != null && win.mWinAnimator.finishDrawingLocked()) {
if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
getDefaultDisplayContentLocked().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
+ if (win.mAppToken != null) {
+ // App has drawn something to its windows, we're no longer animating with
+ // the saved surfaces. If the user exits now, we only want to save again
+ // if allDrawn is true.
+ win.mAppToken.mAnimatingWithSavedSurface = false;
+ }
win.setDisplayLayoutNeeded();
mWindowPlacerLocked.requestTraversal();
}
@@ -2862,46 +2884,44 @@
"applyAnimation: atoken=" + atoken);
// Determine the visible rect to calculate the thumbnail clip
- WindowState win = atoken.findMainWindow();
- Rect containingFrame = new Rect(0, 0, width, height);
- Rect contentInsets = new Rect();
- Rect appFrame = new Rect(0, 0, width, height);
+ final WindowState win = atoken.findMainWindow();
+ final Rect frame = new Rect(0, 0, width, height);
+ final Rect insets = new Rect();
Rect surfaceInsets = null;
final boolean fullscreen = win != null && win.isFullscreen(width, height);
final boolean freeform = win != null && win.inFreeformWorkspace();
+ final boolean docked = win != null && win.inDockedWorkspace();
if (win != null) {
// Containing frame will usually cover the whole screen, including dialog windows.
// For freeform workspace windows it will not cover the whole screen and it also
// won't exactly match the final freeform window frame (e.g. when overlapping with
// the status bar). In that case we need to use the final frame.
if (freeform) {
- containingFrame.set(win.mFrame);
+ frame.set(win.mFrame);
} else {
- containingFrame.set(win.mContainingFrame);
+ frame.set(win.mContainingFrame);
}
surfaceInsets = win.getAttrs().surfaceInsets;
- if (fullscreen) {
+ if (fullscreen || docked) {
// For fullscreen windows use the window frames and insets to set the thumbnail
- // clip. For none-fullscreen windows we use the app display region so the clip
+ // clip. For non-fullscreen windows we use the app display region so the clip
// isn't affected by the window insets.
- contentInsets.set(win.mContentInsets);
- appFrame.set(win.mFrame);
- } else {
- appFrame.set(containingFrame);
+ insets.set(win.mContentInsets);
}
}
- final int containingWidth = containingFrame.width();
- final int containingHeight = containingFrame.height();
if (atoken.mLaunchTaskBehind) {
// Differentiate the two animations. This one which is briefly on the screen
// gets the !enter animation, and the other activity which remains on the
// screen gets the enter animation. Both appear in the mOpeningApps set.
enter = false;
}
- Animation a = mAppTransition.loadAnimation(lp, transit, enter, containingWidth,
- containingHeight, mCurConfiguration.orientation, containingFrame, contentInsets,
- surfaceInsets, appFrame, isVoiceInteraction, freeform, atoken.mTask.mTaskId);
+ if (DEBUG_APP_TRANSITIONS) Slog.d(TAG, "Loading animation for app transition."
+ + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
+ + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
+ Animation a = mAppTransition.loadAnimation(lp, transit, enter,
+ mCurConfiguration.orientation, frame, insets, surfaceInsets, isVoiceInteraction,
+ freeform, atoken.mTask.mTaskId);
if (a != null) {
if (DEBUG_ANIM) {
RuntimeException e = null;
@@ -2911,6 +2931,8 @@
}
Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e);
}
+ final int containingWidth = frame.width();
+ final int containingHeight = frame.height();
atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight,
mAppTransition.canSkipFirstFrame());
}
@@ -3909,7 +3931,7 @@
// transition animation
// * or this is an opening app and windows are being replaced.
if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) ||
- (visible && wtoken.mReplacingWindow)) {
+ (visible && wtoken.mWillReplaceWindow)) {
boolean changed = false;
if (DEBUG_APP_TRANSITIONS) Slog.v(
TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden
@@ -4657,7 +4679,7 @@
throw new IllegalArgumentException("resizeStack: stackId " + stackId
+ " not found.");
}
- if (stack.setBounds(bounds, configs, taskBounds)) {
+ if (stack.setBounds(bounds, configs, taskBounds) && stack.isVisibleLocked()) {
stack.resizeWindows();
stack.getDisplayContent().layoutNeeded = true;
mWindowPlacerLocked.performSurfacePlacement();
@@ -4710,6 +4732,26 @@
}
}
+ /**
+ * Starts deferring layout passes. Useful when doing multiple changes but to optimize
+ * performance, only one layout pass should be done. This can be called multiple times, and
+ * layouting will be resumed once the last caller has called {@link #continueSurfaceLayout}
+ */
+ public void deferSurfaceLayout() {
+ synchronized (mWindowMap) {
+ mWindowPlacerLocked.deferLayout();
+ }
+ }
+
+ /**
+ * Resumes layout passes after deferring them. See {@link #deferSurfaceLayout()}
+ */
+ public void continueSurfaceLayout() {
+ synchronized (mWindowMap) {
+ mWindowPlacerLocked.continueLayout();
+ }
+ }
+
public void getTaskBounds(int taskId, Rect bounds) {
synchronized (mWindowMap) {
Task task = mTaskIdToTask.get(taskId);
@@ -6972,7 +7014,7 @@
+ " asbinder=" + window.asBinder());
}
- final int callerPid = Binder.getCallingPid();
+ final int callerUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
IBinder token = null;
@@ -6997,6 +7039,7 @@
final IBinder winBinder = window.asBinder();
token = new Binder();
mDragState = new DragState(this, token, surface, flags, winBinder);
+ mDragState.mUid = callerUid;
token = mDragState.mToken = new Binder();
// 5 second timeout for this window to actually begin the drag
@@ -8346,7 +8389,7 @@
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
- if (wtoken.mIsExiting && !wtoken.mReplacingWindow) {
+ if (wtoken.mIsExiting && !wtoken.mWillReplaceWindow) {
continue;
}
i = reAddAppWindowsLocked(displayContent, i, wtoken);
@@ -8416,7 +8459,8 @@
} else if (wtoken != null) {
winAnimator.mAnimLayer =
w.mLayer + wtoken.mAppAnimator.animLayerAdjustment;
- if (wtoken.mReplacingWindow && wtoken.mAnimateReplacingWindow) {
+ if (wtoken.mWillReplaceWindow && wtoken.mAnimateReplacingWindow &&
+ wtoken.mReplacingWindow != w) {
// We know that we will be animating a relaunching window in the near future,
// which will receive a z-order increase. We want the replaced window to
// immediately receive the same treatment, e.g. to be above the dock divider.
@@ -9962,7 +10006,9 @@
Slog.w(TAG, "Attempted to set replacing window on non-existing app token " + token);
return;
}
- appWindowToken.mReplacingWindow = true;
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Marking app token " + appWindowToken
+ + " as replacing window.");
+ appWindowToken.mWillReplaceWindow = true;
appWindowToken.mAnimateReplacingWindow = animate;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7ea64f1..e9ea3a8 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -28,6 +28,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
@@ -35,6 +36,7 @@
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.graphics.Point;
import android.os.PowerManager;
@@ -55,7 +57,6 @@
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteException;
@@ -446,7 +447,8 @@
+ WindowManagerService.TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.subWindowTypeToLayerLw(a.type);
mAttachedWindow = attachedWindow;
- if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to " + mAttachedWindow);
+ if (WindowManagerService.DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + this + " to "
+ + mAttachedWindow);
final WindowList childWindows = mAttachedWindow.mChildWindows;
final int numChildWindows = childWindows.size();
@@ -552,7 +554,7 @@
@Override
public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
Rect osf) {
- if (mAppToken != null && mAppToken.mReplacingWindow
+ if (mAppToken != null && mAppToken.mWillReplaceWindow
&& (mExiting || !mAppToken.mReplacingRemoveRequested)) {
// This window is being replaced and either already got information that it's being
// removed or we are still waiting for some information. Because of this we don't
@@ -1307,10 +1309,12 @@
void maybeRemoveReplacedWindow() {
AppWindowToken token = mAppToken;
- if (token != null && token.mReplacingWindow) {
- token.mReplacingWindow = false;
+ if (token != null && token.mWillReplaceWindow && token.mReplacingWindow == this) {
+ if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replacing window: " + this);
+ token.mWillReplaceWindow = false;
token.mAnimateReplacingWindow = false;
token.mReplacingRemoveRequested = false;
+ token.mReplacingWindow = null;
for (int i = token.allAppWindows.size() - 1; i >= 0; i--) {
WindowState win = token.allAppWindows.get(i);
if (win.mExiting) {
@@ -1501,6 +1505,31 @@
return mExiting || (mService.mClosingApps.contains(mAppToken));
}
+ boolean saveSurfaceIfNeeded() {
+ if (ActivityManager.isLowRamDeviceStatic()) {
+ // Don't save surfaces on Svelte devices.
+ return false;
+ }
+
+ Task task = getTask();
+ if (task == null || task.inHomeStack()) {
+ // Don't save surfaces for home stack apps. These usually resume and draw
+ // first frame very fast. Saving surfaces are mostly a waste of memory.
+ return false;
+ }
+
+ // We want to save surface if the app's windows are "allDrawn", or if we're
+ // currently animating with save surfaces. (If the app didn't even finish
+ // drawing when the user exits, but we have a saved surface from last time,
+ // we still want to keep that surface.)
+ if (mAppToken.allDrawn || mAppToken.mAnimatingWithSavedSurface) {
+ mAppToken.mHasSavedSurface = true;
+ return true;
+ }
+
+ return false;
+ }
+
@Override
public boolean isDefaultDisplay() {
final DisplayContent displayContent = getDisplayContent();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1754123..68c39ba 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -38,7 +38,6 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
import android.os.RemoteException;
@@ -536,6 +535,7 @@
mDrawState = COMMIT_DRAW_PENDING;
return true;
}
+
return false;
}
@@ -555,7 +555,8 @@
mDrawState = READY_TO_SHOW;
boolean result = false;
final AppWindowToken atoken = mWin.mAppToken;
- if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ if (atoken == null || atoken.allDrawn || atoken.mAnimatingWithSavedSurface ||
+ mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = performShowLocked();
}
if (mDestroyPreservedSurfaceUponRedraw && result) {
@@ -996,12 +997,16 @@
}
void destroySurfaceLocked() {
- if (mWin.mAppToken != null && mWin == mWin.mAppToken.startingWindow) {
- mWin.mAppToken.startingDisplayed = false;
+ final AppWindowToken wtoken = mWin.mAppToken;
+ if (wtoken != null) {
+ wtoken.mHasSavedSurface = false;
+ wtoken.mAnimatingWithSavedSurface = false;
+ if (mWin == wtoken.startingWindow) {
+ wtoken.startingDisplayed = false;
+ }
}
if (mSurfaceControl != null) {
-
int i = mWin.mChildWindows.size();
while (i > 0) {
i--;
@@ -1428,7 +1433,7 @@
// living in a different stack. If we suddenly crop it to the new stack bounds, it might
// get cut off. We don't want it to happen, so we let it ignore the stack bounds until it
// gets removed. The window that will replace it will abide them.
- if (appToken != null && appToken.mCropWindowsToStack && !appToken.mReplacingWindow) {
+ if (appToken != null && appToken.mCropWindowsToStack && !appToken.mWillReplaceWindow) {
TaskStack stack = w.getTask().mStack;
stack.getBounds(mTmpStackBounds);
// When we resize we use the big surface approach, which means we can't trust the
@@ -1854,8 +1859,7 @@
}
}
- if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING
- && mWin.mAppToken != null) {
+ if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING && mWin.mAppToken != null) {
mWin.mAppToken.firstWindowDrawn = true;
if (mWin.mAppToken.startingData != null) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 0c004b2..b267860 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -17,6 +17,7 @@
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.WindowManagerService.DEBUG;
import static com.android.server.wm.WindowManagerService.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerService.DEBUG_ANIM;
import static com.android.server.wm.WindowManagerService.DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT_REPEATS;
@@ -111,13 +112,34 @@
private int mPreferredModeId = 0;
private boolean mTraversalScheduled;
+ private int mDeferDepth = 0;
public WindowSurfacePlacer(WindowManagerService service) {
mService = service;
mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
}
+ /**
+ * See {@link WindowManagerService#deferSurfaceLayout()}
+ */
+ void deferLayout() {
+ mDeferDepth++;
+ }
+
+ /**
+ * See {@link WindowManagerService#continueSurfaceLayout()}
+ */
+ void continueLayout() {
+ mDeferDepth--;
+ if (mDeferDepth <= 0) {
+ performSurfacePlacement();
+ }
+ }
+
final void performSurfacePlacement() {
+ if (mDeferDepth > 0) {
+ return;
+ }
int loopCount = 6;
do {
mTraversalScheduled = false;
@@ -340,6 +362,10 @@
// Don't remove this window until rotation has completed.
continue;
}
+ // Discard the saved surface if window size is changed, it can't be reused.
+ if (win.mAppToken != null && win.mAppToken.mHasSavedSurface) {
+ win.mWinAnimator.destroySurfaceLocked();
+ }
win.reportResized();
mService.mResizingWindows.remove(i);
}
@@ -371,7 +397,9 @@
if (mWallpaperControllerLocked.isWallpaperTarget(win)) {
wallpaperDestroyed = true;
}
- win.mWinAnimator.destroySurfaceLocked();
+ if (!win.saveSurfaceIfNeeded()) {
+ win.mWinAnimator.destroySurfaceLocked();
+ }
} while (i > 0);
mService.mDestroySurface.clear();
}
@@ -1174,6 +1202,18 @@
}
}
createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+
+ if (wtoken.mHasSavedSurface) {
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG,
+ "Early start of animation: " + wtoken + ", allDrawn=" + wtoken.allDrawn);
+
+ for (int j = 0; j < wtoken.windows.size(); j++) {
+ WindowState ws = wtoken.windows.get(j);
+ ws.mWinAnimator.mDrawState = WindowStateAnimator.READY_TO_SHOW;
+ }
+ wtoken.mHasSavedSurface = false;
+ wtoken.mAnimatingWithSavedSurface = true;
+ }
}
AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null :
@@ -1219,6 +1259,10 @@
+ wtoken.allDrawn + " startingDisplayed="
+ wtoken.startingDisplayed + " startingMoved="
+ wtoken.startingMoved);
+
+ if (wtoken.mHasSavedSurface || wtoken.mAnimatingWithSavedSurface) {
+ continue;
+ }
if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
return false;
}