Merge "QS: Update layout to show gear all the time" into nyc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 427d9b3..01aa843 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -299,13 +299,13 @@
field public static final int backgroundTint = 16843883; // 0x101046b
field public static final int backgroundTintMode = 16843884; // 0x101046c
field public static final int backupAgent = 16843391; // 0x101027f
- field public static final int backupInForeground = 16844058; // 0x101051a
+ field public static final int backupInForeground = 16844059; // 0x101051b
field public static final int banner = 16843762; // 0x10103f2
field public static final int baseline = 16843548; // 0x101031c
field public static final int baselineAlignBottom = 16843042; // 0x1010122
field public static final int baselineAligned = 16843046; // 0x1010126
field public static final int baselineAlignedChildIndex = 16843047; // 0x1010127
- field public static final int bitmap = 16844054; // 0x1010516
+ field public static final int bitmap = 16844055; // 0x1010517
field public static final int borderlessButtonStyle = 16843563; // 0x101032b
field public static final int bottom = 16843184; // 0x10101b0
field public static final int bottomBright = 16842957; // 0x10100cd
@@ -324,7 +324,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
@@ -335,9 +335,9 @@
field public static final int calendarTextColor = 16843931; // 0x101049b
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
- field public static final int canControlMagnification = 16844039; // 0x1010507
- field public static final int canPerformGestures = 16844045; // 0x101050d
- field public static final int canRecord = 16844060; // 0x101051c
+ field public static final int canControlMagnification = 16844040; // 0x1010508
+ field public static final int canPerformGestures = 16844046; // 0x101050e
+ field public static final int canRecord = 16844061; // 0x101051d
field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -377,7 +377,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
@@ -414,18 +414,18 @@
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
- field public static final int contentInsetEndWithActions = 16844067; // 0x1010523
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
- field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
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
field public static final int controlY2 = 16843775; // 0x10103ff
- field public static final int countDown = 16844059; // 0x101051b
+ field public static final int countDown = 16844060; // 0x101051c
field public static final int country = 16843962; // 0x10104ba
field public static final int cropToPadding = 16843043; // 0x1010123
field public static final int cursorVisible = 16843090; // 0x1010152
@@ -443,7 +443,7 @@
field public static final deprecated int dayOfWeekTextAppearance = 16843925; // 0x1010495
field public static final int debuggable = 16842767; // 0x101000f
field public static final int defaultHeight = 16844021; // 0x10104f5
- field public static final int defaultToDeviceProtectedStorage = 16844036; // 0x1010504
+ field public static final int defaultToDeviceProtectedStorage = 16844037; // 0x1010505
field public static final int defaultValue = 16843245; // 0x10101ed
field public static final int defaultWidth = 16844020; // 0x10104f4
field public static final int delay = 16843212; // 0x10101cc
@@ -463,7 +463,7 @@
field public static final int dialogTheme = 16843528; // 0x1010308
field public static final int dialogTitle = 16843250; // 0x10101f2
field public static final int digits = 16843110; // 0x1010166
- field public static final int directBootAware = 16844037; // 0x1010505
+ field public static final int directBootAware = 16844038; // 0x1010506
field public static final int direction = 16843217; // 0x10101d1
field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1
field public static final int directionPriority = 16843218; // 0x10101d2
@@ -511,12 +511,12 @@
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
- field public static final int enableVrMode = 16844069; // 0x1010525
+ field public static final int enableVrMode = 16844072; // 0x1010528
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
- field public static final int endX = 16844050; // 0x1010512
- field public static final int endY = 16844051; // 0x1010513
+ field public static final int endX = 16844051; // 0x1010513
+ field public static final int endY = 16844052; // 0x1010514
field public static final deprecated int endYear = 16843133; // 0x101017d
field public static final int enterFadeDuration = 16843532; // 0x101030c
field public static final int entries = 16842930; // 0x10100b2
@@ -536,7 +536,7 @@
field public static final int expandableListViewStyle = 16842863; // 0x101006f
field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
field public static final int exported = 16842768; // 0x1010010
- field public static final int externalService = 16844046; // 0x101050e
+ field public static final int externalService = 16844047; // 0x101050f
field public static final int extraTension = 16843371; // 0x101026b
field public static final int extractNativeLibs = 16844010; // 0x10104ea
field public static final int factor = 16843219; // 0x10101d3
@@ -561,7 +561,7 @@
field public static final int fillBefore = 16843196; // 0x10101bc
field public static final int fillColor = 16843780; // 0x1010404
field public static final int fillEnabled = 16843343; // 0x101024f
- field public static final int fillType = 16844062; // 0x101051e
+ field public static final int fillType = 16844064; // 0x1010520
field public static final int fillViewport = 16843130; // 0x101017a
field public static final int filter = 16843035; // 0x101011b
field public static final int filterTouchesWhenObscured = 16843460; // 0x10102c4
@@ -577,7 +577,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
- field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -654,8 +654,8 @@
field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
field public static final int horizontalSpacing = 16843028; // 0x1010114
field public static final int host = 16842792; // 0x1010028
- field public static final int hotSpotX = 16844055; // 0x1010517
- field public static final int hotSpotY = 16844056; // 0x1010518
+ field public static final int hotSpotX = 16844056; // 0x1010518
+ field public static final int hotSpotY = 16844057; // 0x1010519
field public static final int hyphenationFrequency = 16843998; // 0x10104de
field public static final int icon = 16842754; // 0x1010002
field public static final int iconPreview = 16843337; // 0x1010249
@@ -742,7 +742,7 @@
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field public static final int labelTextSize = 16843317; // 0x1010235
- field public static final int languageTag = 16844040; // 0x1010508
+ field public static final int languageTag = 16844041; // 0x1010509
field public static final int largeHeap = 16843610; // 0x101035a
field public static final int largeScreens = 16843398; // 0x1010286
field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -800,7 +800,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
@@ -833,7 +833,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
@@ -876,25 +876,26 @@
field public static final int navigationMode = 16843471; // 0x10102cf
field public static final int negativeButtonText = 16843254; // 0x10101f6
field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
- field public static final int networkSecurityConfig = 16844071; // 0x1010527
+ field public static final int networkSecurityConfig = 16844074; // 0x101052a
field public static final int nextFocusDown = 16842980; // 0x10100e4
field public static final int nextFocusForward = 16843580; // 0x101033c
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
field public static final int numColumns = 16843032; // 0x1010118
field public static final int numStars = 16843076; // 0x1010144
- field public static final int numberPickerStyle = 16844068; // 0x1010524
+ field public static final int numberPickerStyle = 16844071; // 0x1010527
field public static final int numbersBackgroundColor = 16843938; // 0x10104a2
field public static final int numbersInnerTextColor = 16844001; // 0x10104e1
field public static final int numbersSelectorColor = 16843939; // 0x10104a3
field public static final int numbersTextColor = 16843937; // 0x10104a1
field public static final deprecated int numeric = 16843109; // 0x1010165
field public static final int numericShortcut = 16843236; // 0x10101e4
- field public static final int offset = 16844052; // 0x1010514
+ field public static final int offset = 16844053; // 0x1010515
field public static final int onClick = 16843375; // 0x101026f
field public static final int oneshot = 16843159; // 0x1010197
field public static final int opacity = 16843550; // 0x101031e
@@ -941,13 +942,13 @@
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
- field public static final int pointerIcon = 16844041; // 0x1010509
+ field public static final int pointerIcon = 16844042; // 0x101050a
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
field public static final int popupBackground = 16843126; // 0x1010176
field public static final int popupCharacters = 16843332; // 0x1010244
field public static final int popupElevation = 16843916; // 0x101048c
- field public static final int popupEnterTransition = 16844063; // 0x101051f
- field public static final int popupExitTransition = 16844064; // 0x1010520
+ field public static final int popupEnterTransition = 16844065; // 0x1010521
+ field public static final int popupExitTransition = 16844066; // 0x1010522
field public static final int popupKeyboard = 16843331; // 0x1010243
field public static final int popupLayout = 16843323; // 0x101023b
field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -956,7 +957,7 @@
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
- field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
+ field public static final int preferenceFragmentStyle = 16844039; // 0x1010507
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
@@ -1024,7 +1025,7 @@
field public static final int resizeClip = 16843983; // 0x10104cf
field public static final int resizeMode = 16843619; // 0x1010363
field public static final int resizeable = 16843405; // 0x101028d
- field public static final int resizeableActivity = 16844022; // 0x10104f6
+ field public static final int resizeableActivity = 16844023; // 0x10104f7
field public static final int resource = 16842789; // 0x1010025
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1106,6 +1107,15 @@
field public static final int shareInterpolator = 16843195; // 0x10101bb
field public static final int sharedUserId = 16842763; // 0x101000b
field public static final int sharedUserLabel = 16843361; // 0x1010261
+ field public static final int shortcutCategories = 16844081; // 0x1010531
+ field public static final int shortcutDisabledMessage = 16844080; // 0x1010530
+ field public static final int shortcutIcon = 16844077; // 0x101052d
+ field public static final int shortcutId = 16844075; // 0x101052b
+ field public static final int shortcutIntentAction = 16844082; // 0x1010532
+ field public static final int shortcutIntentData = 16844083; // 0x1010533
+ field public static final int shortcutRank = 16844076; // 0x101052c
+ field public static final int shortcutText = 16844079; // 0x101052f
+ field public static final int shortcutTitle = 16844078; // 0x101052e
field public static final int shouldDisableView = 16843246; // 0x10101ee
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
@@ -1144,8 +1154,8 @@
field public static final int startColor = 16843165; // 0x101019d
field public static final int startDelay = 16843746; // 0x10103e2
field public static final int startOffset = 16843198; // 0x10101be
- field public static final int startX = 16844048; // 0x1010510
- field public static final int startY = 16844049; // 0x1010511
+ field public static final int startX = 16844049; // 0x1010511
+ field public static final int startY = 16844050; // 0x1010512
field public static final deprecated int startYear = 16843132; // 0x101017c
field public static final int stateListAnimator = 16843848; // 0x1010448
field public static final int stateNotNeeded = 16842774; // 0x1010016
@@ -1202,8 +1212,8 @@
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 supportsLocalInteraction = 16844047; // 0x101050f
- field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
+ field public static final int supportsLocalInteraction = 16844048; // 0x1010510
+ 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
@@ -1252,7 +1262,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
@@ -1310,9 +1320,9 @@
field public static final int thumbTint = 16843889; // 0x1010471
field public static final int thumbTintMode = 16843890; // 0x1010472
field public static final int thumbnail = 16843429; // 0x10102a5
- field public static final int tickMark = 16844042; // 0x101050a
- field public static final int tickMarkTint = 16844043; // 0x101050b
- field public static final int tickMarkTintMode = 16844044; // 0x101050c
+ field public static final int tickMark = 16844043; // 0x101050b
+ field public static final int tickMarkTint = 16844044; // 0x101050c
+ field public static final int tickMarkTintMode = 16844045; // 0x101050d
field public static final int tileMode = 16843265; // 0x1010201
field public static final int tileModeX = 16843895; // 0x1010477
field public static final int tileModeY = 16843896; // 0x1010478
@@ -1324,11 +1334,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
@@ -1367,7 +1377,7 @@
field public static final int trimPathEnd = 16843785; // 0x1010409
field public static final int trimPathOffset = 16843786; // 0x101040a
field public static final int trimPathStart = 16843784; // 0x1010408
- field public static final int tunerCount = 16844061; // 0x101051d
+ field public static final int tunerCount = 16844062; // 0x101051e
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -1375,7 +1385,7 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
- field public static final int use32bitAbi = 16844053; // 0x1010515
+ field public static final int use32bitAbi = 16844054; // 0x1010516
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
@@ -1387,7 +1397,7 @@
field public static final int valueType = 16843488; // 0x10102e0
field public static final int variablePadding = 16843157; // 0x1010195
field public static final int vendor = 16843751; // 0x10103e7
- field public static final int version = 16844057; // 0x1010519
+ field public static final int version = 16844058; // 0x101051a
field public static final int versionCode = 16843291; // 0x101021b
field public static final int versionName = 16843292; // 0x101021c
field public static final int verticalCorrection = 16843322; // 0x101023a
@@ -1431,7 +1441,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
@@ -9501,8 +9511,8 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(int);
- method public android.graphics.drawable.Drawable getShortcutIconDrawable(int);
+ method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
@@ -9534,13 +9544,14 @@
public static class LauncherApps.ShortcutQuery {
ctor public LauncherApps.ShortcutQuery();
- method public void setActivity(android.content.ComponentName);
- method public void setChangedSince(long);
- method public void setPackage(java.lang.String);
- method public void setQueryFlags(int);
- method public void setShortcutIds(java.util.List<java.lang.String>);
+ method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
+ method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+ method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
+ method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
+ method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_GET_MANIFEST = 8; // 0x8
field public static final int FLAG_GET_PINNED = 2; // 0x2
}
@@ -10044,29 +10055,30 @@
public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
- method public android.content.ComponentName getActivityComponent();
+ method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
- method public java.lang.String getDisabledMessage();
+ method public java.lang.CharSequence getDisabledMessage();
method public int getDisabledMessageResId();
method public android.os.PersistableBundle getExtras();
method public int getIconResourceId();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
- method public java.lang.String getPackageName();
+ method public java.lang.String getPackage();
method public int getRank();
- method public java.lang.String getText();
+ method public java.lang.CharSequence getText();
method public int getTextResId();
- method public java.lang.String getTitle();
+ method public java.lang.CharSequence getTitle();
method public int getTitleResId();
method public android.os.UserHandle getUserHandle();
method public boolean hasIconFile();
method public boolean hasIconResource();
method public boolean hasKeyFieldsOnly();
method public boolean hasStringResourcesResolved();
- method public boolean isDisabled();
method public boolean isDynamic();
- method public boolean isFromManifest();
+ method public boolean isEnabled();
+ method public boolean isImmutable();
+ method public boolean isManifestShortcut();
method public boolean isPinned();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
@@ -10075,10 +10087,11 @@
field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
field public static final int FLAG_DISABLED = 64; // 0x40
field public static final int FLAG_DYNAMIC = 1; // 0x1
- field public static final int FLAG_FROM_MANIFEST = 32; // 0x20
field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+ field public static final int FLAG_IMMUTABLE = 256; // 0x100
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
+ field public static final int FLAG_MANIFEST = 32; // 0x20
field public static final int FLAG_PINNED = 2; // 0x2
field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
@@ -10087,19 +10100,16 @@
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context);
method public android.content.pm.ShortcutInfo build();
- method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setDisabledMessageResId(int);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
method public android.content.pm.ShortcutInfo.Builder setRank(int);
method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setTextResId(int);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setTitleResId(int);
}
public class ShortcutManager {
@@ -10107,9 +10117,12 @@
method public void disableShortcuts(java.util.List<java.lang.String>);
method public void disableShortcuts(java.util.List<java.lang.String>, int);
method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.String);
+ method public void enableShortcuts(java.util.List<java.lang.String>);
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
- method public int getIconMaxDimensions();
- method public int getMaxDynamicShortcutCount();
+ method public int getIconMaxHeight();
+ method public int getIconMaxWidth();
+ method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
+ method public int getMaxShortcutCountForActivity();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
method public long getRateLimitResetTime();
method public int getRemainingCallCount();
diff --git a/api/removed.txt b/api/removed.txt
index 8119834..0ff2476 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,3 +1,17 @@
+package android {
+
+ public static final class R.attr {
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
+ }
+
+ public static final class R.styleable {
+ field public static final int AndroidManifestLayout_minimalHeight = 6; // 0x6
+ field public static final int AndroidManifestLayout_minimalWidth = 5; // 0x5
+ }
+
+}
+
package android.app {
public class Notification implements android.os.Parcelable {
@@ -454,6 +468,11 @@
package com.android.internal {
+ public static final class R.attr {
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
+ }
+
public static final class R.styleable {
field public static final int AndroidManifestLayout_minimalHeight = 6; // 0x6
field public static final int AndroidManifestLayout_minimalWidth = 5; // 0x5
diff --git a/api/system-current.txt b/api/system-current.txt
index 3e8d6fa..c18b916 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -405,13 +405,13 @@
field public static final int backgroundTint = 16843883; // 0x101046b
field public static final int backgroundTintMode = 16843884; // 0x101046c
field public static final int backupAgent = 16843391; // 0x101027f
- field public static final int backupInForeground = 16844058; // 0x101051a
+ field public static final int backupInForeground = 16844059; // 0x101051b
field public static final int banner = 16843762; // 0x10103f2
field public static final int baseline = 16843548; // 0x101031c
field public static final int baselineAlignBottom = 16843042; // 0x1010122
field public static final int baselineAligned = 16843046; // 0x1010126
field public static final int baselineAlignedChildIndex = 16843047; // 0x1010127
- field public static final int bitmap = 16844054; // 0x1010516
+ field public static final int bitmap = 16844055; // 0x1010517
field public static final int borderlessButtonStyle = 16843563; // 0x101032b
field public static final int bottom = 16843184; // 0x10101b0
field public static final int bottomBright = 16842957; // 0x10100cd
@@ -430,7 +430,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
@@ -441,9 +441,9 @@
field public static final int calendarTextColor = 16843931; // 0x101049b
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
- field public static final int canControlMagnification = 16844039; // 0x1010507
- field public static final int canPerformGestures = 16844045; // 0x101050d
- field public static final int canRecord = 16844060; // 0x101051c
+ field public static final int canControlMagnification = 16844040; // 0x1010508
+ field public static final int canPerformGestures = 16844046; // 0x101050e
+ field public static final int canRecord = 16844061; // 0x101051d
field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -483,7 +483,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
@@ -520,18 +520,18 @@
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
- field public static final int contentInsetEndWithActions = 16844067; // 0x1010523
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
- field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
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
field public static final int controlY2 = 16843775; // 0x10103ff
- field public static final int countDown = 16844059; // 0x101051b
+ field public static final int countDown = 16844060; // 0x101051c
field public static final int country = 16843962; // 0x10104ba
field public static final int cropToPadding = 16843043; // 0x1010123
field public static final int cursorVisible = 16843090; // 0x1010152
@@ -549,7 +549,7 @@
field public static final deprecated int dayOfWeekTextAppearance = 16843925; // 0x1010495
field public static final int debuggable = 16842767; // 0x101000f
field public static final int defaultHeight = 16844021; // 0x10104f5
- field public static final int defaultToDeviceProtectedStorage = 16844036; // 0x1010504
+ field public static final int defaultToDeviceProtectedStorage = 16844037; // 0x1010505
field public static final int defaultValue = 16843245; // 0x10101ed
field public static final int defaultWidth = 16844020; // 0x10104f4
field public static final int delay = 16843212; // 0x10101cc
@@ -569,7 +569,7 @@
field public static final int dialogTheme = 16843528; // 0x1010308
field public static final int dialogTitle = 16843250; // 0x10101f2
field public static final int digits = 16843110; // 0x1010166
- field public static final int directBootAware = 16844037; // 0x1010505
+ field public static final int directBootAware = 16844038; // 0x1010506
field public static final int direction = 16843217; // 0x10101d1
field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1
field public static final int directionPriority = 16843218; // 0x10101d2
@@ -617,12 +617,12 @@
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
- field public static final int enableVrMode = 16844069; // 0x1010525
+ field public static final int enableVrMode = 16844072; // 0x1010528
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
- field public static final int endX = 16844050; // 0x1010512
- field public static final int endY = 16844051; // 0x1010513
+ field public static final int endX = 16844051; // 0x1010513
+ field public static final int endY = 16844052; // 0x1010514
field public static final deprecated int endYear = 16843133; // 0x101017d
field public static final int enterFadeDuration = 16843532; // 0x101030c
field public static final int entries = 16842930; // 0x10100b2
@@ -642,7 +642,7 @@
field public static final int expandableListViewStyle = 16842863; // 0x101006f
field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
field public static final int exported = 16842768; // 0x1010010
- field public static final int externalService = 16844046; // 0x101050e
+ field public static final int externalService = 16844047; // 0x101050f
field public static final int extraTension = 16843371; // 0x101026b
field public static final int extractNativeLibs = 16844010; // 0x10104ea
field public static final int factor = 16843219; // 0x10101d3
@@ -667,7 +667,7 @@
field public static final int fillBefore = 16843196; // 0x10101bc
field public static final int fillColor = 16843780; // 0x1010404
field public static final int fillEnabled = 16843343; // 0x101024f
- field public static final int fillType = 16844062; // 0x101051e
+ field public static final int fillType = 16844064; // 0x1010520
field public static final int fillViewport = 16843130; // 0x101017a
field public static final int filter = 16843035; // 0x101011b
field public static final int filterTouchesWhenObscured = 16843460; // 0x10102c4
@@ -683,7 +683,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
- field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -760,8 +760,8 @@
field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
field public static final int horizontalSpacing = 16843028; // 0x1010114
field public static final int host = 16842792; // 0x1010028
- field public static final int hotSpotX = 16844055; // 0x1010517
- field public static final int hotSpotY = 16844056; // 0x1010518
+ field public static final int hotSpotX = 16844056; // 0x1010518
+ field public static final int hotSpotY = 16844057; // 0x1010519
field public static final int hyphenationFrequency = 16843998; // 0x10104de
field public static final int icon = 16842754; // 0x1010002
field public static final int iconPreview = 16843337; // 0x1010249
@@ -848,7 +848,7 @@
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field public static final int labelTextSize = 16843317; // 0x1010235
- field public static final int languageTag = 16844040; // 0x1010508
+ field public static final int languageTag = 16844041; // 0x1010509
field public static final int largeHeap = 16843610; // 0x101035a
field public static final int largeScreens = 16843398; // 0x1010286
field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -906,7 +906,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
@@ -939,7 +939,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
@@ -982,25 +982,26 @@
field public static final int navigationMode = 16843471; // 0x10102cf
field public static final int negativeButtonText = 16843254; // 0x10101f6
field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
- field public static final int networkSecurityConfig = 16844071; // 0x1010527
+ field public static final int networkSecurityConfig = 16844074; // 0x101052a
field public static final int nextFocusDown = 16842980; // 0x10100e4
field public static final int nextFocusForward = 16843580; // 0x101033c
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
field public static final int numColumns = 16843032; // 0x1010118
field public static final int numStars = 16843076; // 0x1010144
- field public static final int numberPickerStyle = 16844068; // 0x1010524
+ field public static final int numberPickerStyle = 16844071; // 0x1010527
field public static final int numbersBackgroundColor = 16843938; // 0x10104a2
field public static final int numbersInnerTextColor = 16844001; // 0x10104e1
field public static final int numbersSelectorColor = 16843939; // 0x10104a3
field public static final int numbersTextColor = 16843937; // 0x10104a1
field public static final deprecated int numeric = 16843109; // 0x1010165
field public static final int numericShortcut = 16843236; // 0x10101e4
- field public static final int offset = 16844052; // 0x1010514
+ field public static final int offset = 16844053; // 0x1010515
field public static final int onClick = 16843375; // 0x101026f
field public static final int oneshot = 16843159; // 0x1010197
field public static final int opacity = 16843550; // 0x101031e
@@ -1047,13 +1048,13 @@
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
- field public static final int pointerIcon = 16844041; // 0x1010509
+ field public static final int pointerIcon = 16844042; // 0x101050a
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
field public static final int popupBackground = 16843126; // 0x1010176
field public static final int popupCharacters = 16843332; // 0x1010244
field public static final int popupElevation = 16843916; // 0x101048c
- field public static final int popupEnterTransition = 16844063; // 0x101051f
- field public static final int popupExitTransition = 16844064; // 0x1010520
+ field public static final int popupEnterTransition = 16844065; // 0x1010521
+ field public static final int popupExitTransition = 16844066; // 0x1010522
field public static final int popupKeyboard = 16843331; // 0x1010243
field public static final int popupLayout = 16843323; // 0x101023b
field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -1062,7 +1063,7 @@
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
- field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
+ field public static final int preferenceFragmentStyle = 16844039; // 0x1010507
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
@@ -1130,7 +1131,7 @@
field public static final int resizeClip = 16843983; // 0x10104cf
field public static final int resizeMode = 16843619; // 0x1010363
field public static final int resizeable = 16843405; // 0x101028d
- field public static final int resizeableActivity = 16844022; // 0x10104f6
+ field public static final int resizeableActivity = 16844023; // 0x10104f7
field public static final int resource = 16842789; // 0x1010025
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1216,6 +1217,15 @@
field public static final int shareInterpolator = 16843195; // 0x10101bb
field public static final int sharedUserId = 16842763; // 0x101000b
field public static final int sharedUserLabel = 16843361; // 0x1010261
+ field public static final int shortcutCategories = 16844081; // 0x1010531
+ field public static final int shortcutDisabledMessage = 16844080; // 0x1010530
+ field public static final int shortcutIcon = 16844077; // 0x101052d
+ field public static final int shortcutId = 16844075; // 0x101052b
+ field public static final int shortcutIntentAction = 16844082; // 0x1010532
+ field public static final int shortcutIntentData = 16844083; // 0x1010533
+ field public static final int shortcutRank = 16844076; // 0x101052c
+ field public static final int shortcutText = 16844079; // 0x101052f
+ field public static final int shortcutTitle = 16844078; // 0x101052e
field public static final int shouldDisableView = 16843246; // 0x10101ee
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
@@ -1254,8 +1264,8 @@
field public static final int startColor = 16843165; // 0x101019d
field public static final int startDelay = 16843746; // 0x10103e2
field public static final int startOffset = 16843198; // 0x10101be
- field public static final int startX = 16844048; // 0x1010510
- field public static final int startY = 16844049; // 0x1010511
+ field public static final int startX = 16844049; // 0x1010511
+ field public static final int startY = 16844050; // 0x1010512
field public static final deprecated int startYear = 16843132; // 0x101017c
field public static final int stateListAnimator = 16843848; // 0x1010448
field public static final int stateNotNeeded = 16842774; // 0x1010016
@@ -1312,8 +1322,8 @@
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 supportsLocalInteraction = 16844047; // 0x101050f
- field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
+ field public static final int supportsLocalInteraction = 16844048; // 0x1010510
+ 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
@@ -1362,7 +1372,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
@@ -1420,9 +1430,9 @@
field public static final int thumbTint = 16843889; // 0x1010471
field public static final int thumbTintMode = 16843890; // 0x1010472
field public static final int thumbnail = 16843429; // 0x10102a5
- field public static final int tickMark = 16844042; // 0x101050a
- field public static final int tickMarkTint = 16844043; // 0x101050b
- field public static final int tickMarkTintMode = 16844044; // 0x101050c
+ field public static final int tickMark = 16844043; // 0x101050b
+ field public static final int tickMarkTint = 16844044; // 0x101050c
+ field public static final int tickMarkTintMode = 16844045; // 0x101050d
field public static final int tileMode = 16843265; // 0x1010201
field public static final int tileModeX = 16843895; // 0x1010477
field public static final int tileModeY = 16843896; // 0x1010478
@@ -1434,11 +1444,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
@@ -1477,7 +1487,7 @@
field public static final int trimPathEnd = 16843785; // 0x1010409
field public static final int trimPathOffset = 16843786; // 0x101040a
field public static final int trimPathStart = 16843784; // 0x1010408
- field public static final int tunerCount = 16844061; // 0x101051d
+ field public static final int tunerCount = 16844062; // 0x101051e
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -1485,7 +1495,7 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
- field public static final int use32bitAbi = 16844053; // 0x1010515
+ field public static final int use32bitAbi = 16844054; // 0x1010516
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
@@ -1497,7 +1507,7 @@
field public static final int valueType = 16843488; // 0x10102e0
field public static final int variablePadding = 16843157; // 0x1010195
field public static final int vendor = 16843751; // 0x10103e7
- field public static final int version = 16844057; // 0x1010519
+ field public static final int version = 16844058; // 0x101051a
field public static final int versionCode = 16843291; // 0x101021b
field public static final int versionName = 16843292; // 0x101021c
field public static final int verticalCorrection = 16843322; // 0x101023a
@@ -1541,7 +1551,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
@@ -9854,8 +9864,8 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(int);
- method public android.graphics.drawable.Drawable getShortcutIconDrawable(int);
+ method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
@@ -9887,13 +9897,14 @@
public static class LauncherApps.ShortcutQuery {
ctor public LauncherApps.ShortcutQuery();
- method public void setActivity(android.content.ComponentName);
- method public void setChangedSince(long);
- method public void setPackage(java.lang.String);
- method public void setQueryFlags(int);
- method public void setShortcutIds(java.util.List<java.lang.String>);
+ method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
+ method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+ method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
+ method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
+ method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_GET_MANIFEST = 8; // 0x8
field public static final int FLAG_GET_PINNED = 2; // 0x2
}
@@ -10467,29 +10478,30 @@
public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
- method public android.content.ComponentName getActivityComponent();
+ method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
- method public java.lang.String getDisabledMessage();
+ method public java.lang.CharSequence getDisabledMessage();
method public int getDisabledMessageResId();
method public android.os.PersistableBundle getExtras();
method public int getIconResourceId();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
- method public java.lang.String getPackageName();
+ method public java.lang.String getPackage();
method public int getRank();
- method public java.lang.String getText();
+ method public java.lang.CharSequence getText();
method public int getTextResId();
- method public java.lang.String getTitle();
+ method public java.lang.CharSequence getTitle();
method public int getTitleResId();
method public android.os.UserHandle getUserHandle();
method public boolean hasIconFile();
method public boolean hasIconResource();
method public boolean hasKeyFieldsOnly();
method public boolean hasStringResourcesResolved();
- method public boolean isDisabled();
method public boolean isDynamic();
- method public boolean isFromManifest();
+ method public boolean isEnabled();
+ method public boolean isImmutable();
+ method public boolean isManifestShortcut();
method public boolean isPinned();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
@@ -10498,10 +10510,11 @@
field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
field public static final int FLAG_DISABLED = 64; // 0x40
field public static final int FLAG_DYNAMIC = 1; // 0x1
- field public static final int FLAG_FROM_MANIFEST = 32; // 0x20
field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+ field public static final int FLAG_IMMUTABLE = 256; // 0x100
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
+ field public static final int FLAG_MANIFEST = 32; // 0x20
field public static final int FLAG_PINNED = 2; // 0x2
field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
@@ -10510,19 +10523,16 @@
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context);
method public android.content.pm.ShortcutInfo build();
- method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setDisabledMessageResId(int);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
method public android.content.pm.ShortcutInfo.Builder setRank(int);
method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setTextResId(int);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setTitleResId(int);
}
public class ShortcutManager {
@@ -10530,9 +10540,12 @@
method public void disableShortcuts(java.util.List<java.lang.String>);
method public void disableShortcuts(java.util.List<java.lang.String>, int);
method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.String);
+ method public void enableShortcuts(java.util.List<java.lang.String>);
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
- method public int getIconMaxDimensions();
- method public int getMaxDynamicShortcutCount();
+ method public int getIconMaxHeight();
+ method public int getIconMaxWidth();
+ method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
+ method public int getMaxShortcutCountForActivity();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
method public long getRateLimitResetTime();
method public int getRemainingCallCount();
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 7f18d6a..715312f 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,3 +1,17 @@
+package android {
+
+ public static final class R.attr {
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
+ }
+
+ public static final class R.styleable {
+ field public static final int AndroidManifestLayout_minimalHeight = 6; // 0x6
+ field public static final int AndroidManifestLayout_minimalWidth = 5; // 0x5
+ }
+
+}
+
package android.app {
public class Notification implements android.os.Parcelable {
@@ -452,6 +466,11 @@
package com.android.internal {
+ public static final class R.attr {
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
+ }
+
public static final class R.styleable {
field public static final int AndroidManifestLayout_minimalHeight = 6; // 0x6
field public static final int AndroidManifestLayout_minimalWidth = 5; // 0x5
diff --git a/api/test-current.txt b/api/test-current.txt
index 1448c6c..7de82b3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -299,13 +299,13 @@
field public static final int backgroundTint = 16843883; // 0x101046b
field public static final int backgroundTintMode = 16843884; // 0x101046c
field public static final int backupAgent = 16843391; // 0x101027f
- field public static final int backupInForeground = 16844058; // 0x101051a
+ field public static final int backupInForeground = 16844059; // 0x101051b
field public static final int banner = 16843762; // 0x10103f2
field public static final int baseline = 16843548; // 0x101031c
field public static final int baselineAlignBottom = 16843042; // 0x1010122
field public static final int baselineAligned = 16843046; // 0x1010126
field public static final int baselineAlignedChildIndex = 16843047; // 0x1010127
- field public static final int bitmap = 16844054; // 0x1010516
+ field public static final int bitmap = 16844055; // 0x1010517
field public static final int borderlessButtonStyle = 16843563; // 0x101032b
field public static final int bottom = 16843184; // 0x10101b0
field public static final int bottomBright = 16842957; // 0x10100cd
@@ -324,7 +324,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
@@ -335,9 +335,9 @@
field public static final int calendarTextColor = 16843931; // 0x101049b
field public static final int calendarViewShown = 16843596; // 0x101034c
field public static final int calendarViewStyle = 16843613; // 0x101035d
- field public static final int canControlMagnification = 16844039; // 0x1010507
- field public static final int canPerformGestures = 16844045; // 0x101050d
- field public static final int canRecord = 16844060; // 0x101051c
+ field public static final int canControlMagnification = 16844040; // 0x1010508
+ field public static final int canPerformGestures = 16844046; // 0x101050e
+ field public static final int canRecord = 16844061; // 0x101051d
field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
field public static final int canRequestFilterKeyEvents = 16843737; // 0x10103d9
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
@@ -377,7 +377,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
@@ -414,18 +414,18 @@
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
- field public static final int contentInsetEndWithActions = 16844067; // 0x1010523
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
- field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
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
field public static final int controlY2 = 16843775; // 0x10103ff
- field public static final int countDown = 16844059; // 0x101051b
+ field public static final int countDown = 16844060; // 0x101051c
field public static final int country = 16843962; // 0x10104ba
field public static final int cropToPadding = 16843043; // 0x1010123
field public static final int cursorVisible = 16843090; // 0x1010152
@@ -443,7 +443,7 @@
field public static final deprecated int dayOfWeekTextAppearance = 16843925; // 0x1010495
field public static final int debuggable = 16842767; // 0x101000f
field public static final int defaultHeight = 16844021; // 0x10104f5
- field public static final int defaultToDeviceProtectedStorage = 16844036; // 0x1010504
+ field public static final int defaultToDeviceProtectedStorage = 16844037; // 0x1010505
field public static final int defaultValue = 16843245; // 0x10101ed
field public static final int defaultWidth = 16844020; // 0x10104f4
field public static final int delay = 16843212; // 0x10101cc
@@ -463,7 +463,7 @@
field public static final int dialogTheme = 16843528; // 0x1010308
field public static final int dialogTitle = 16843250; // 0x10101f2
field public static final int digits = 16843110; // 0x1010166
- field public static final int directBootAware = 16844037; // 0x1010505
+ field public static final int directBootAware = 16844038; // 0x1010506
field public static final int direction = 16843217; // 0x10101d1
field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1
field public static final int directionPriority = 16843218; // 0x10101d2
@@ -511,12 +511,12 @@
field public static final int elevation = 16843840; // 0x1010440
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
- field public static final int enableVrMode = 16844069; // 0x1010525
+ field public static final int enableVrMode = 16844072; // 0x1010528
field public static final int enabled = 16842766; // 0x101000e
field public static final int end = 16843996; // 0x10104dc
field public static final int endColor = 16843166; // 0x101019e
- field public static final int endX = 16844050; // 0x1010512
- field public static final int endY = 16844051; // 0x1010513
+ field public static final int endX = 16844051; // 0x1010513
+ field public static final int endY = 16844052; // 0x1010514
field public static final deprecated int endYear = 16843133; // 0x101017d
field public static final int enterFadeDuration = 16843532; // 0x101030c
field public static final int entries = 16842930; // 0x10100b2
@@ -536,7 +536,7 @@
field public static final int expandableListViewStyle = 16842863; // 0x101006f
field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
field public static final int exported = 16842768; // 0x1010010
- field public static final int externalService = 16844046; // 0x101050e
+ field public static final int externalService = 16844047; // 0x101050f
field public static final int extraTension = 16843371; // 0x101026b
field public static final int extractNativeLibs = 16844010; // 0x10104ea
field public static final int factor = 16843219; // 0x10101d3
@@ -561,7 +561,7 @@
field public static final int fillBefore = 16843196; // 0x10101bc
field public static final int fillColor = 16843780; // 0x1010404
field public static final int fillEnabled = 16843343; // 0x101024f
- field public static final int fillType = 16844062; // 0x101051e
+ field public static final int fillType = 16844064; // 0x1010520
field public static final int fillViewport = 16843130; // 0x101017a
field public static final int filter = 16843035; // 0x101011b
field public static final int filterTouchesWhenObscured = 16843460; // 0x10102c4
@@ -577,7 +577,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
- field public static final int forceHasOverlappingRendering = 16844065; // 0x1010521
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -654,8 +654,8 @@
field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
field public static final int horizontalSpacing = 16843028; // 0x1010114
field public static final int host = 16842792; // 0x1010028
- field public static final int hotSpotX = 16844055; // 0x1010517
- field public static final int hotSpotY = 16844056; // 0x1010518
+ field public static final int hotSpotX = 16844056; // 0x1010518
+ field public static final int hotSpotY = 16844057; // 0x1010519
field public static final int hyphenationFrequency = 16843998; // 0x10104de
field public static final int icon = 16842754; // 0x1010002
field public static final int iconPreview = 16843337; // 0x1010249
@@ -742,7 +742,7 @@
field public static final int label = 16842753; // 0x1010001
field public static final int labelFor = 16843718; // 0x10103c6
field public static final int labelTextSize = 16843317; // 0x1010235
- field public static final int languageTag = 16844040; // 0x1010508
+ field public static final int languageTag = 16844041; // 0x1010509
field public static final int largeHeap = 16843610; // 0x101035a
field public static final int largeScreens = 16843398; // 0x1010286
field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -800,7 +800,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
@@ -833,7 +833,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
@@ -876,25 +876,26 @@
field public static final int navigationMode = 16843471; // 0x10102cf
field public static final int negativeButtonText = 16843254; // 0x10101f6
field public static final int nestedScrollingEnabled = 16843830; // 0x1010436
- field public static final int networkSecurityConfig = 16844071; // 0x1010527
+ field public static final int networkSecurityConfig = 16844074; // 0x101052a
field public static final int nextFocusDown = 16842980; // 0x10100e4
field public static final int nextFocusForward = 16843580; // 0x101033c
field public static final int nextFocusLeft = 16842977; // 0x10100e1
field public static final int nextFocusRight = 16842978; // 0x10100e2
field public static final int nextFocusUp = 16842979; // 0x10100e3
+ field public static final int nfcAntennaPositionDrawable = 16844063; // 0x101051f
field public static final int noHistory = 16843309; // 0x101022d
field public static final int normalScreens = 16843397; // 0x1010285
field public static final int notificationTimeout = 16843651; // 0x1010383
field public static final int numColumns = 16843032; // 0x1010118
field public static final int numStars = 16843076; // 0x1010144
- field public static final int numberPickerStyle = 16844068; // 0x1010524
+ field public static final int numberPickerStyle = 16844071; // 0x1010527
field public static final int numbersBackgroundColor = 16843938; // 0x10104a2
field public static final int numbersInnerTextColor = 16844001; // 0x10104e1
field public static final int numbersSelectorColor = 16843939; // 0x10104a3
field public static final int numbersTextColor = 16843937; // 0x10104a1
field public static final deprecated int numeric = 16843109; // 0x1010165
field public static final int numericShortcut = 16843236; // 0x10101e4
- field public static final int offset = 16844052; // 0x1010514
+ field public static final int offset = 16844053; // 0x1010515
field public static final int onClick = 16843375; // 0x101026f
field public static final int oneshot = 16843159; // 0x1010197
field public static final int opacity = 16843550; // 0x101031e
@@ -941,13 +942,13 @@
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
- field public static final int pointerIcon = 16844041; // 0x1010509
+ field public static final int pointerIcon = 16844042; // 0x101050a
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
field public static final int popupBackground = 16843126; // 0x1010176
field public static final int popupCharacters = 16843332; // 0x1010244
field public static final int popupElevation = 16843916; // 0x101048c
- field public static final int popupEnterTransition = 16844063; // 0x101051f
- field public static final int popupExitTransition = 16844064; // 0x1010520
+ field public static final int popupEnterTransition = 16844065; // 0x1010521
+ field public static final int popupExitTransition = 16844066; // 0x1010522
field public static final int popupKeyboard = 16843331; // 0x1010243
field public static final int popupLayout = 16843323; // 0x101023b
field public static final int popupMenuStyle = 16843520; // 0x1010300
@@ -956,7 +957,7 @@
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
- field public static final int preferenceFragmentStyle = 16844038; // 0x1010506
+ field public static final int preferenceFragmentStyle = 16844039; // 0x1010507
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
@@ -1024,7 +1025,7 @@
field public static final int resizeClip = 16843983; // 0x10104cf
field public static final int resizeMode = 16843619; // 0x1010363
field public static final int resizeable = 16843405; // 0x101028d
- field public static final int resizeableActivity = 16844022; // 0x10104f6
+ field public static final int resizeableActivity = 16844023; // 0x10104f7
field public static final int resource = 16842789; // 0x1010025
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
@@ -1106,6 +1107,15 @@
field public static final int shareInterpolator = 16843195; // 0x10101bb
field public static final int sharedUserId = 16842763; // 0x101000b
field public static final int sharedUserLabel = 16843361; // 0x1010261
+ field public static final int shortcutCategories = 16844081; // 0x1010531
+ field public static final int shortcutDisabledMessage = 16844080; // 0x1010530
+ field public static final int shortcutIcon = 16844077; // 0x101052d
+ field public static final int shortcutId = 16844075; // 0x101052b
+ field public static final int shortcutIntentAction = 16844082; // 0x1010532
+ field public static final int shortcutIntentData = 16844083; // 0x1010533
+ field public static final int shortcutRank = 16844076; // 0x101052c
+ field public static final int shortcutText = 16844079; // 0x101052f
+ field public static final int shortcutTitle = 16844078; // 0x101052e
field public static final int shouldDisableView = 16843246; // 0x10101ee
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
@@ -1144,8 +1154,8 @@
field public static final int startColor = 16843165; // 0x101019d
field public static final int startDelay = 16843746; // 0x10103e2
field public static final int startOffset = 16843198; // 0x10101be
- field public static final int startX = 16844048; // 0x1010510
- field public static final int startY = 16844049; // 0x1010511
+ field public static final int startX = 16844049; // 0x1010511
+ field public static final int startY = 16844050; // 0x1010512
field public static final deprecated int startYear = 16843132; // 0x101017c
field public static final int stateListAnimator = 16843848; // 0x1010448
field public static final int stateNotNeeded = 16842774; // 0x1010016
@@ -1202,8 +1212,8 @@
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 supportsLocalInteraction = 16844047; // 0x101050f
- field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
+ field public static final int supportsLocalInteraction = 16844048; // 0x1010510
+ 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
@@ -1252,7 +1262,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
@@ -1310,9 +1320,9 @@
field public static final int thumbTint = 16843889; // 0x1010471
field public static final int thumbTintMode = 16843890; // 0x1010472
field public static final int thumbnail = 16843429; // 0x10102a5
- field public static final int tickMark = 16844042; // 0x101050a
- field public static final int tickMarkTint = 16844043; // 0x101050b
- field public static final int tickMarkTintMode = 16844044; // 0x101050c
+ field public static final int tickMark = 16844043; // 0x101050b
+ field public static final int tickMarkTint = 16844044; // 0x101050c
+ field public static final int tickMarkTintMode = 16844045; // 0x101050d
field public static final int tileMode = 16843265; // 0x1010201
field public static final int tileModeX = 16843895; // 0x1010477
field public static final int tileModeY = 16843896; // 0x1010478
@@ -1324,11 +1334,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
@@ -1367,7 +1377,7 @@
field public static final int trimPathEnd = 16843785; // 0x1010409
field public static final int trimPathOffset = 16843786; // 0x101040a
field public static final int trimPathStart = 16843784; // 0x1010408
- field public static final int tunerCount = 16844061; // 0x101051d
+ field public static final int tunerCount = 16844062; // 0x101051e
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -1375,7 +1385,7 @@
field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
field public static final int unselectedAlpha = 16843278; // 0x101020e
field public static final int updatePeriodMillis = 16843344; // 0x1010250
- field public static final int use32bitAbi = 16844053; // 0x1010515
+ field public static final int use32bitAbi = 16844054; // 0x1010516
field public static final int useDefaultMargins = 16843641; // 0x1010379
field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
field public static final int useLevel = 16843167; // 0x101019f
@@ -1387,7 +1397,7 @@
field public static final int valueType = 16843488; // 0x10102e0
field public static final int variablePadding = 16843157; // 0x1010195
field public static final int vendor = 16843751; // 0x10103e7
- field public static final int version = 16844057; // 0x1010519
+ field public static final int version = 16844058; // 0x101051a
field public static final int versionCode = 16843291; // 0x101021b
field public static final int versionName = 16843292; // 0x101021c
field public static final int verticalCorrection = 16843322; // 0x101023a
@@ -1431,7 +1441,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
@@ -9513,8 +9523,8 @@
public class LauncherApps {
ctor public LauncherApps(android.content.Context);
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(int);
- method public android.graphics.drawable.Drawable getShortcutIconDrawable(int);
+ method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
+ method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
method public android.os.ParcelFileDescriptor getShortcutIconFd(android.content.pm.ShortcutInfo);
method public android.os.ParcelFileDescriptor getShortcutIconFd(java.lang.String, java.lang.String, android.os.UserHandle);
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
@@ -9546,13 +9556,14 @@
public static class LauncherApps.ShortcutQuery {
ctor public LauncherApps.ShortcutQuery();
- method public void setActivity(android.content.ComponentName);
- method public void setChangedSince(long);
- method public void setPackage(java.lang.String);
- method public void setQueryFlags(int);
- method public void setShortcutIds(java.util.List<java.lang.String>);
+ method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
+ method public android.content.pm.LauncherApps.ShortcutQuery setChangedSince(long);
+ method public android.content.pm.LauncherApps.ShortcutQuery setPackage(java.lang.String);
+ method public android.content.pm.LauncherApps.ShortcutQuery setQueryFlags(int);
+ method public android.content.pm.LauncherApps.ShortcutQuery setShortcutIds(java.util.List<java.lang.String>);
field public static final int FLAG_GET_DYNAMIC = 1; // 0x1
field public static final int FLAG_GET_KEY_FIELDS_ONLY = 4; // 0x4
+ field public static final int FLAG_GET_MANIFEST = 8; // 0x8
field public static final int FLAG_GET_PINNED = 2; // 0x2
}
@@ -10057,29 +10068,30 @@
public final class ShortcutInfo implements android.os.Parcelable {
method public int describeContents();
- method public android.content.ComponentName getActivityComponent();
+ method public android.content.ComponentName getActivity();
method public java.util.Set<java.lang.String> getCategories();
- method public java.lang.String getDisabledMessage();
+ method public java.lang.CharSequence getDisabledMessage();
method public int getDisabledMessageResId();
method public android.os.PersistableBundle getExtras();
method public int getIconResourceId();
method public java.lang.String getId();
method public android.content.Intent getIntent();
method public long getLastChangedTimestamp();
- method public java.lang.String getPackageName();
+ method public java.lang.String getPackage();
method public int getRank();
- method public java.lang.String getText();
+ method public java.lang.CharSequence getText();
method public int getTextResId();
- method public java.lang.String getTitle();
+ method public java.lang.CharSequence getTitle();
method public int getTitleResId();
method public android.os.UserHandle getUserHandle();
method public boolean hasIconFile();
method public boolean hasIconResource();
method public boolean hasKeyFieldsOnly();
method public boolean hasStringResourcesResolved();
- method public boolean isDisabled();
method public boolean isDynamic();
- method public boolean isFromManifest();
+ method public boolean isEnabled();
+ method public boolean isImmutable();
+ method public boolean isManifestShortcut();
method public boolean isPinned();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CLONE_REMOVE_FOR_CREATOR = 1; // 0x1
@@ -10088,10 +10100,11 @@
field public static final android.os.Parcelable.Creator<android.content.pm.ShortcutInfo> CREATOR;
field public static final int FLAG_DISABLED = 64; // 0x40
field public static final int FLAG_DYNAMIC = 1; // 0x1
- field public static final int FLAG_FROM_MANIFEST = 32; // 0x20
field public static final int FLAG_HAS_ICON_FILE = 8; // 0x8
field public static final int FLAG_HAS_ICON_RES = 4; // 0x4
+ field public static final int FLAG_IMMUTABLE = 256; // 0x100
field public static final int FLAG_KEY_FIELDS_ONLY = 16; // 0x10
+ field public static final int FLAG_MANIFEST = 32; // 0x20
field public static final int FLAG_PINNED = 2; // 0x2
field public static final int FLAG_STRINGS_RESOLVED = 128; // 0x80
field public static final java.lang.String SHORTCUT_CATEGORY_CONVERSATION = "android.shortcut.conversation";
@@ -10100,19 +10113,16 @@
public static class ShortcutInfo.Builder {
ctor public ShortcutInfo.Builder(android.content.Context);
method public android.content.pm.ShortcutInfo build();
- method public android.content.pm.ShortcutInfo.Builder setActivityComponent(android.content.ComponentName);
+ method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setDisabledMessageResId(int);
method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
method public android.content.pm.ShortcutInfo.Builder setRank(int);
method public android.content.pm.ShortcutInfo.Builder setText(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setTextResId(int);
method public android.content.pm.ShortcutInfo.Builder setTitle(java.lang.String);
- method public android.content.pm.ShortcutInfo.Builder setTitleResId(int);
}
public class ShortcutManager {
@@ -10121,9 +10131,12 @@
method public void disableShortcuts(java.util.List<java.lang.String>);
method public void disableShortcuts(java.util.List<java.lang.String>, int);
method public void disableShortcuts(java.util.List<java.lang.String>, java.lang.String);
+ method public void enableShortcuts(java.util.List<java.lang.String>);
method public java.util.List<android.content.pm.ShortcutInfo> getDynamicShortcuts();
- method public int getIconMaxDimensions();
- method public int getMaxDynamicShortcutCount();
+ method public int getIconMaxHeight();
+ method public int getIconMaxWidth();
+ method public java.util.List<android.content.pm.ShortcutInfo> getManifestShortcuts();
+ method public int getMaxShortcutCountForActivity();
method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
method public long getRateLimitResetTime();
method public int getRemainingCallCount();
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 8119834..0ff2476 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -1,3 +1,17 @@
+package android {
+
+ public static final class R.attr {
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
+ }
+
+ public static final class R.styleable {
+ field public static final int AndroidManifestLayout_minimalHeight = 6; // 0x6
+ field public static final int AndroidManifestLayout_minimalWidth = 5; // 0x5
+ }
+
+}
+
package android.app {
public class Notification implements android.os.Parcelable {
@@ -454,6 +468,11 @@
package com.android.internal {
+ public static final class R.attr {
+ field public static final int minimalHeight = 16844067; // 0x1010523
+ field public static final int minimalWidth = 16844022; // 0x10104f6
+ }
+
public static final class R.styleable {
field public static final int AndroidManifestLayout_minimalHeight = 6; // 0x6
field public static final int AndroidManifestLayout_minimalWidth = 5; // 0x5
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 1d10860..5c4b979 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -227,10 +227,11 @@
/**
* Constructs and returns an ObjectAnimator that animates between int values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation).
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
@@ -274,10 +275,11 @@
/**
* Constructs and returns an ObjectAnimator that animates between int values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation).
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
@@ -383,10 +385,11 @@
/**
* Constructs and returns an ObjectAnimator that animates between color values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation).
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
@@ -403,10 +406,11 @@
/**
* Constructs and returns an ObjectAnimator that animates between color values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation).
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
@@ -422,10 +426,11 @@
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation).
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated. This object should
* have a public method on it called <code>setName()</code>, where <code>name</code> is
@@ -469,10 +474,11 @@
/**
* Constructs and returns an ObjectAnimator that animates between float values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation).
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
*
* @param target The object whose property is to be animated.
* @param property The property being animated.
@@ -580,10 +586,11 @@
/**
* Constructs and returns an ObjectAnimator that animates between Object values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation).
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
*
* <p><strong>Note:</strong> The values are stored as references to the original
* objects, which means that changes to those objects after this method is called will
@@ -635,10 +642,11 @@
/**
* Constructs and returns an ObjectAnimator that animates between Object values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation).
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
*
* <p><strong>Note:</strong> The values are stored as references to the original
* objects, which means that changes to those objects after this method is called will
@@ -665,12 +673,13 @@
/**
* Constructs and returns an ObjectAnimator that animates between Object values. A single
- * value implies that that value is the one being animated to. Two values imply starting
- * and ending values. More than two values imply a starting value, values to animate through
- * along the way, and an ending value (these values will be distributed evenly across
- * the duration of the animation). This variant supplies a <code>TypeConverter</code> to
- * convert from the animated values to the type of the property. If only one value is
- * supplied, the <code>TypeConverter</code> must be a
+ * value implies that that value is the one being animated to, in which case the start value
+ * will be derived from the property being animated and the target object when {@link #start()}
+ * is called for the first time. Two values imply starting and ending values. More than two
+ * values imply a starting value, values to animate through along the way, and an ending value
+ * (these values will be distributed evenly across the duration of the animation).
+ * This variant supplies a <code>TypeConverter</code> to convert from the animated values to the
+ * type of the property. If only one value is supplied, the <code>TypeConverter</code> must be a
* {@link android.animation.BidirectionalTypeConverter} to retrieve the current value.
*
* <p><strong>Note:</strong> The values are stored as references to the original
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4b8d9ee..3a70a4c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.content.ComponentName;
+import android.content.IIntentSender;
import android.os.IBinder;
import android.service.voice.IVoiceInteractionSession;
@@ -144,4 +145,10 @@
* Kill foreground apps from the specified user.
*/
public abstract void killForegroundAppsForUser(int userHandle);
+
+ /**
+ * Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions
+ * such as Power Save mode.
+ */
+ public abstract void setPendingIntentWhitelistDuration(IIntentSender target, long duration);
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index cd9a05b..f12c284 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -6966,8 +6966,8 @@
reply.recycle();
}
- public void notifyLockedProfile(@UserIdInt int userId) throws RemoteException
- {
+ @Override
+ public void notifyLockedProfile(@UserIdInt int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -6978,6 +6978,7 @@
reply.recycle();
}
+ @Override
public void startConfirmDeviceCredentialIntent(Intent intent) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -6989,6 +6990,7 @@
reply.recycle();
}
+ @Override
public int sendIntentSender(IIntentSender target, int code, Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
throws RemoteException {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0cc44e7..e108dd6e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4323,18 +4323,23 @@
// experience
bigTextText = mBuilder.processLegacyText(text);
}
- contentView.setTextViewText(R.id.big_text, bigTextText);
- contentView.setViewVisibility(R.id.big_text,
- TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
- contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines());
- contentView.setBoolean(R.id.big_text, "setHasImage", mBuilder.mN.mLargeIcon != null);
+ applyBigTextContentView(mBuilder, contentView, bigTextText);
return contentView;
}
- private int calculateMaxLines() {
+ static void applyBigTextContentView(Builder builder,
+ RemoteViews contentView, CharSequence bigTextText) {
+ contentView.setTextViewText(R.id.big_text, bigTextText);
+ contentView.setViewVisibility(R.id.big_text,
+ TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
+ contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines(builder));
+ contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.mLargeIcon != null);
+ }
+
+ private static int calculateMaxLines(Builder builder) {
int lineCount = MAX_LINES;
- boolean hasActions = mBuilder.mActions.size() > 0;
+ boolean hasActions = builder.mActions.size() > 0;
if (hasActions) {
lineCount -= LINES_CONSUMED_BY_ACTIONS;
}
@@ -4532,6 +4537,16 @@
: mConversationTitle;
boolean hasTitle = !TextUtils.isEmpty(title);
+ if (!hasTitle && mMessages.size() == 1) {
+ CharSequence sender = mMessages.get(0).mSender;
+ CharSequence text = mMessages.get(0).mText;
+ RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
+ mBuilder.getBigTextLayoutResource(),
+ false /* progress */, sender, null /* text */);
+ BigTextStyle.applyBigTextContentView(mBuilder, contentView, text);
+ return contentView;
+ }
+
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
mBuilder.getMessagingLayoutResource(),
false /* hasProgress */,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fc579a9..8b3eac5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -285,6 +285,12 @@
public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080;
/**
+ * @hide Flag for {@link #bindService}: allows application hosting service to manage whitelists
+ * such as temporary allowing a {@code PendingIntent} to bypass Power Save mode.
+ */
+ public static final int BIND_ALLOW_WHITELIST_MANAGEMENT = 0x01000000;
+
+ /**
* @hide Flag for {@link #bindService}: Like {@link #BIND_FOREGROUND_SERVICE},
* but only applies while the device is awake.
*/
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 03f83d6..4bbbdee 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -459,23 +459,19 @@
/**
* Ask the package manager to perform dex-opt (if needed) on the given
- * package and for the given instruction set if it already hasn't done
- * so.
- *
- * If the supplied instructionSet is null, the package manager will use
- * the packages default instruction set.
+ * package if it already hasn't done so.
*
* In most cases, apps are dexopted in advance and this function will
* be a no-op.
*/
- boolean performDexOptIfNeeded(String packageName, String instructionSet);
+ boolean performDexOptIfNeeded(String packageName);
/**
* Ask the package manager to perform a dex-opt for the given reason. The package
* manager will map the reason to a compiler filter according to the current system
* configuration.
*/
- boolean performDexOpt(String packageName, String instructionSet, boolean checkProfiles,
+ boolean performDexOpt(String packageName, boolean checkProfiles,
int compileReason, boolean force);
/**
* Ask the package manager to perform a dex-opt with the given compiler filter.
@@ -483,7 +479,7 @@
* Note: exposed only for the shell command to allow moving packages explicitly to a
* definite state.
*/
- boolean performDexOptMode(String packageName, String instructionSet, boolean checkProfiles,
+ boolean performDexOptMode(String packageName, boolean checkProfiles,
String targetCompilerFilter, boolean force);
void forceDexOpt(String packageName);
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 06200bf..1c373f9 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -28,6 +28,8 @@
ParceledListSlice getDynamicShortcuts(String packageName, int userId);
+ ParceledListSlice getManifestShortcuts(String packageName, int userId);
+
boolean addDynamicShortcuts(String packageName, in ParceledListSlice shortcutInfoList,
int userId);
@@ -42,6 +44,8 @@
void disableShortcuts(String packageName, in List shortcutIds, String disabledMessage,
int disabledMessageResId, int userId);
+ void enableShortcuts(String packageName, in List shortcutIds, int userId);
+
int getMaxDynamicShortcutCount(String packageName, int userId);
int getRemainingCallCount(String packageName, int userId);
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index acd85cb..6cb50fc 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -24,7 +24,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -35,8 +40,10 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DisplayMetrics;
import android.util.Log;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -188,6 +195,11 @@
public static final int FLAG_GET_PINNED = 1 << 1;
/**
+ * Include manifest shortcuts in the result.
+ */
+ public static final int FLAG_GET_MANIFEST = 1 << 3;
+
+ /**
* Requests "key" fields only. See {@link ShortcutInfo#hasKeyFieldsOnly()} for which
* fields are available.
*/
@@ -198,6 +210,7 @@
value = {
FLAG_GET_DYNAMIC,
FLAG_GET_PINNED,
+ FLAG_GET_MANIFEST,
FLAG_GET_KEY_FIELDS_ONLY,
})
@Retention(RetentionPolicy.SOURCE)
@@ -224,39 +237,44 @@
* If non-zero, returns only shortcuts that have been added or updated since the timestamp,
* which is a milliseconds since the Epoch.
*/
- public void setChangedSince(long changedSince) {
+ public ShortcutQuery setChangedSince(long changedSince) {
mChangedSince = changedSince;
+ return this;
}
/**
* If non-null, returns only shortcuts from the package.
*/
- public void setPackage(@Nullable String packageName) {
+ public ShortcutQuery setPackage(@Nullable String packageName) {
mPackage = packageName;
+ return this;
}
/**
* If non-null, return only the specified shortcuts by ID. When setting this field,
* a packange name must also be set with {@link #setPackage}.
*/
- public void setShortcutIds(@Nullable List<String> shortcutIds) {
+ public ShortcutQuery setShortcutIds(@Nullable List<String> shortcutIds) {
mShortcutIds = shortcutIds;
+ return this;
}
/**
- * If non-null, returns only shortcuts associated with the activity, which are
- * {@link ShortcutInfo}s that have null {@link ShortcutInfo#getActivityComponent()}, or
- * {@link ShortcutInfo#getActivityComponent()} equals to {@code activity}.
+ * If non-null, returns only shortcuts associated with the activity; i.e.
+ * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal
+ * to {@code activity}.
*/
- public void setActivity(@Nullable ComponentName activity) {
+ public ShortcutQuery setActivity(@Nullable ComponentName activity) {
mActivity = activity;
+ return this;
}
/**
* Set query options.
*/
- public void setQueryFlags(@QueryFlags int queryFlags) {
+ public ShortcutQuery setQueryFlags(@QueryFlags int queryFlags) {
mQueryFlags = queryFlags;
+ return this;
}
}
@@ -525,7 +543,7 @@
*/
public ParcelFileDescriptor getShortcutIconFd(
@NonNull ShortcutInfo shortcut) {
- return getShortcutIconFd(shortcut.getPackageName(), shortcut.getId(),
+ return getShortcutIconFd(shortcut.getPackage(), shortcut.getId(),
shortcut.getUserId());
}
@@ -555,14 +573,61 @@
}
}
- /** TODO Javadoc */
- public Drawable getShortcutIconDrawable(int density) {
- throw new RuntimeException("TODO implement it");
+ /**
+ * Returns the icon for this shortcut, without any badging for the profile.
+ *
+ * @param density The preferred density of the icon, zero for default density. Use
+ * density DPI values from {@link DisplayMetrics}.
+ * @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
+ * @see DisplayMetrics
+ * @return The drawable associated with the shortcut.
+ */
+ public Drawable getShortcutIconDrawable(@NonNull ShortcutInfo shortcut, int density) {
+ if (shortcut.hasIconFile()) {
+ final ParcelFileDescriptor pfd = getShortcutIconFd(shortcut);
+ if (pfd == null) {
+ return null;
+ }
+ try {
+ final Bitmap bmp = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
+ return (bmp == null) ? null : new BitmapDrawable(mContext.getResources(), bmp);
+ } finally {
+ try {
+ pfd.close();
+ } catch (IOException ignore) {
+ }
+ }
+ } else if (shortcut.hasIconResource()) {
+ try {
+ final int resId = shortcut.getIconResourceId();
+ if (resId == 0) {
+ return null; // Shouldn't happen but just in case.
+ }
+ final ApplicationInfo ai = getApplicationInfo(shortcut.getPackage(),
+ /* flags =*/ 0, shortcut.getUserHandle());
+ final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
+ return res.getDrawableForDensity(resId, density);
+ } catch (NameNotFoundException | Resources.NotFoundException e) {
+ return null;
+ }
+ } else {
+ return null; // Has no icon.
+ }
}
- /** TODO Javadoc */
- public Drawable getShortcutBadgedIconDrawable(int density) {
- throw new RuntimeException("TODO implement it");
+ /**
+ * Returns the shortcut icon with badging appropriate for the profile.
+ *
+ * @param density Optional density for the icon, or 0 to use the default density. Use
+ * {@link DisplayMetrics} for DPI values.
+ * @see DisplayMetrics
+ * @return A badged icon for the shortcut.
+ */
+ public Drawable getShortcutBadgedIconDrawable(ShortcutInfo shortcut, int density) {
+ final Drawable originalIcon = getShortcutIconDrawable(shortcut, density);
+
+ return (originalIcon == null) ? null : mContext.getPackageManager().getUserBadgedIcon(
+ originalIcon, shortcut.getUserHandle());
}
/**
@@ -600,7 +665,7 @@
*/
public boolean startShortcut(@NonNull ShortcutInfo shortcut,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
- return startShortcut(shortcut.getPackageName(), shortcut.getId(),
+ return startShortcut(shortcut.getPackage(), shortcut.getId(),
sourceBounds, startActivityOptions,
shortcut.getUserId());
}
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 3648d9e..c83aa22 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -70,7 +70,7 @@
public static final int FLAG_KEY_FIELDS_ONLY = 1 << 4;
/* @hide */
- public static final int FLAG_FROM_MANIFEST = 1 << 5;
+ public static final int FLAG_MANIFEST = 1 << 5;
/* @hide */
public static final int FLAG_DISABLED = 1 << 6;
@@ -78,6 +78,9 @@
/* @hide */
public static final int FLAG_STRINGS_RESOLVED = 1 << 7;
+ /* @hide */
+ public static final int FLAG_IMMUTABLE = 1 << 8;
+
/** @hide */
@IntDef(flag = true,
value = {
@@ -86,9 +89,10 @@
FLAG_HAS_ICON_RES,
FLAG_HAS_ICON_FILE,
FLAG_KEY_FIELDS_ONLY,
- FLAG_FROM_MANIFEST,
+ FLAG_MANIFEST,
FLAG_DISABLED,
FLAG_STRINGS_RESOLVED,
+ FLAG_IMMUTABLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShortcutFlags {}
@@ -133,7 +137,7 @@
private final String mPackageName;
@Nullable
- private ComponentName mActivityComponent;
+ private ComponentName mActivity;
@Nullable
private Icon mIcon;
@@ -141,17 +145,17 @@
private int mTitleResId;
@Nullable
- private String mTitle;
+ private CharSequence mTitle;
private int mTextResId;
@Nullable
- private String mText;
+ private CharSequence mText;
private int mDisabledMessageResId;
@Nullable
- private String mDisabledMessage;
+ private CharSequence mDisabledMessage;
@Nullable
private ArraySet<String> mCategories;
@@ -196,7 +200,7 @@
// Note we can't do other null checks here because SM.updateShortcuts() takes partial
// information.
mPackageName = b.mContext.getPackageName();
- mActivityComponent = b.mActivityComponent;
+ mActivity = b.mActivity;
mIcon = b.mIcon;
mTitle = b.mTitle;
mTitleResId = b.mTitleResId;
@@ -229,7 +233,7 @@
*/
public void enforceMandatoryFields() {
Preconditions.checkStringNotEmpty(mId, "Shortcut ID must be provided");
- Preconditions.checkNotNull(mActivityComponent, "activityComponent must be provided");
+ Preconditions.checkNotNull(mActivity, "activity must be provided");
if (mTitle == null && mTitleResId == 0) {
throw new IllegalArgumentException("Shortcut title must be provided");
}
@@ -250,7 +254,7 @@
mIconResourceId = source.mIconResourceId;
if ((cloneFlags & CLONE_REMOVE_NON_KEY_INFO) == 0) {
- mActivityComponent = source.mActivityComponent;
+ mActivity = source.mActivity;
if ((cloneFlags & CLONE_REMOVE_ICON) == 0) {
mIcon = source.mIcon;
@@ -309,6 +313,17 @@
}
/**
+ * @hide
+ */
+ public void ensureUpdatableWith(ShortcutInfo source) {
+ Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
+ Preconditions.checkState(mId.equals(source.mId), "ID must match");
+ Preconditions.checkState(mPackageName.equals(source.mPackageName),
+ "Package name must match");
+ Preconditions.checkState(!isImmutable(), "Target ShortcutInfo is immutable");
+ }
+
+ /**
* Copy non-null/zero fields from another {@link ShortcutInfo}. Only "public" information
* will be overwritten. The timestamp will be updated.
*
@@ -316,16 +331,15 @@
* - mBitmapPath will not change
* - Current time will be set to timestamp
*
+ * @throws IllegalStateException if source is not compatible.
+ *
* @hide
*/
public void copyNonNullFieldsFrom(ShortcutInfo source) {
- Preconditions.checkState(mUserId == source.mUserId, "Owner User ID must match");
- Preconditions.checkState(mId.equals(source.mId), "ID must match");
- Preconditions.checkState(mPackageName.equals(source.mPackageName),
- "Package name must match");
+ ensureUpdatableWith(source);
- if (source.mActivityComponent != null) {
- mActivityComponent = source.mActivityComponent;
+ if (source.mActivity != null) {
+ mActivity = source.mActivity;
}
if (source.mIcon != null) {
@@ -401,21 +415,21 @@
private String mId;
- private ComponentName mActivityComponent;
+ private ComponentName mActivity;
private Icon mIcon;
private int mTitleResId;
- private String mTitle;
+ private CharSequence mTitle;
private int mTextResId;
- private String mText;
+ private CharSequence mText;
private int mDisabledMessageResId;
- private String mDisabledMessage;
+ private CharSequence mDisabledMessage;
private Set<String> mCategories;
@@ -451,8 +465,8 @@
* a hint to the launcher app about which launcher icon to associate this shortcut with.
*/
@NonNull
- public Builder setActivityComponent(@NonNull ComponentName activityComponent) {
- mActivityComponent = Preconditions.checkNotNull(activityComponent, "activityComponent");
+ public Builder setActivity(@NonNull ComponentName activity) {
+ mActivity = Preconditions.checkNotNull(activity, "activity");
return this;
}
@@ -476,7 +490,7 @@
return this;
}
- /** TODO Javadoc */
+ /** @hide */
public Builder setTitleResId(int titleResId) {
Preconditions.checkState(mTitle == null, "title already set");
mTitleResId = titleResId;
@@ -496,7 +510,7 @@
return this;
}
- /** TODO Javadoc */
+ /** @hide */
public Builder setTextResId(int textResId) {
Preconditions.checkState(mText == null, "text already set");
mTextResId = textResId;
@@ -516,7 +530,7 @@
return this;
}
- /** TODO Javadoc */
+ /** @hide */
public Builder setDisabledMessageResId(int disabledMessageResId) {
Preconditions.checkState(mDisabledMessage == null, "disabledMessage already set");
mDisabledMessageResId = disabledMessageResId;
@@ -595,7 +609,7 @@
* Return the package name of the creator application.
*/
@NonNull
- public String getPackageName() {
+ public String getPackage() {
return mPackageName;
}
@@ -606,11 +620,11 @@
* <p>This has nothing to do with the activity that this shortcut will launch. This is
* a hint to the launcher app that on which launcher icon this shortcut should be shown.
*
- * @see Builder#setActivityComponent
+ * @see Builder#setActivity
*/
@Nullable
- public ComponentName getActivityComponent() {
- return mActivityComponent;
+ public ComponentName getActivity() {
+ return mActivity;
}
/**
@@ -634,7 +648,7 @@
* {@link #hasKeyFieldsOnly()} is true.
*/
@Nullable
- public String getTitle() {
+ public CharSequence getTitle() {
return mTitle;
}
@@ -647,7 +661,7 @@
* Return the shortcut text.
*/
@Nullable
- public String getText() {
+ public CharSequence getText() {
return mText;
}
@@ -660,7 +674,7 @@
* Return the message that should be shown when a shortcut in disabled state is launched.
*/
@Nullable
- public String getDisabledMessage() {
+ public CharSequence getDisabledMessage() {
return mDisabledMessage;
}
@@ -788,18 +802,67 @@
}
/**
- * Return whether a shortcut is published via manifest or not. If true, the shortcut is
- * immutable.
+ * Return whether a shortcut is published via AndroidManifest.xml or not. If {@code true},
+ * it's also {@link #isImmutable()}.
+ *
+ * <p>When an app is upgraded and a shortcut is no longer published from AndroidManifest.xml,
+ * this will be set to {@code false}. If the shortcut is not pinned, then it'll just disappear.
+ * However, if it's pinned, it will still be alive, and {@link #isEnabled()} will be
+ * {@code false} and {@link #isImmutable()} will be {@code true}.
+ *
+ * <p>NOTE this is whether a shortcut is published from the <b>current version's</b>
+ * AndroidManifest.xml.
*/
- public boolean isFromManifest() {
- return hasFlags(FLAG_FROM_MANIFEST);
+ public boolean isManifestShortcut() {
+ return hasFlags(FLAG_MANIFEST);
}
- /** Return whether a shortcut is disabled by publisher or not. */
- public boolean isDisabled() {
+ /**
+ * @return true if pinned but neither dynamic nor manifest.
+ * @hide
+ */
+ public boolean isFloating() {
+ return isPinned() && !(isDynamic() || isManifestShortcut());
+ }
+
+ /** @hide */
+ public boolean isOriginallyFromManifest() {
+ return hasFlags(FLAG_IMMUTABLE);
+ }
+
+ /**
+ * Return if a shortcut is immutable, in which case it cannot be modified with any of
+ * {@link ShortcutManager} APIs.
+ *
+ * <p>All manifest shortcuts are immutable. When a manifest shortcut is pinned and then
+ * disabled because the app is upgraded and its AndroidManifest.xml no longer publishes it,
+ * {@link #isManifestShortcut} returns {@code false}, but it is still immutable.
+ *
+ * <p>All shortcuts originally published via the {@link ShortcutManager} APIs
+ * are all mutable.
+ */
+ public boolean isImmutable() {
+ return hasFlags(FLAG_IMMUTABLE);
+ }
+
+ /**
+ * Returns {@code false} if a shortcut is disabled with
+ * {@link ShortcutManager#disableShortcuts}.
+ */
+ public boolean isEnabled() {
return !hasFlags(FLAG_DISABLED);
}
+ /** @hide */
+ public boolean isAlive() {
+ return hasFlags(FLAG_PINNED) || hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST);
+ }
+
+ /** @hide */
+ public boolean usesQuota() {
+ return hasFlags(FLAG_DYNAMIC) || hasFlags(FLAG_MANIFEST);
+ }
+
/**
* Return whether a shortcut's icon is a resource in the owning package.
*
@@ -833,7 +896,7 @@
* following fields are available.
* <ul>
* <li>{@link #getId()}
- * <li>{@link #getPackageName()}
+ * <li>{@link #getPackage()}
* <li>{@link #getLastChangedTimestamp()}
* <li>{@link #isDynamic()}
* <li>{@link #isPinned()}
@@ -888,19 +951,31 @@
mBitmapPath = bitmapPath;
}
+ /** @hide */
+ public void setDisabledMessageResId(int disabledMessageResId) {
+ mDisabledMessageResId = disabledMessageResId;
+ mDisabledMessage = null;
+ }
+
+ /** @hide */
+ public void setDisabledMessage(String disabledMessage) {
+ mDisabledMessage = disabledMessage;
+ mDisabledMessageResId = 0;
+ }
+
private ShortcutInfo(Parcel source) {
final ClassLoader cl = getClass().getClassLoader();
mUserId = source.readInt();
mId = source.readString();
mPackageName = source.readString();
- mActivityComponent = source.readParcelable(cl);
+ mActivity = source.readParcelable(cl);
mIcon = source.readParcelable(cl);
- mTitle = source.readString();
+ mTitle = source.readCharSequence();
mTitleResId = source.readInt();
- mText = source.readString();
+ mText = source.readCharSequence();
mTextResId = source.readInt();
- mDisabledMessage = source.readString();
+ mDisabledMessage = source.readCharSequence();
mDisabledMessageResId = source.readInt();
mIntent = source.readParcelable(cl);
mIntentPersistableExtras = source.readParcelable(cl);
@@ -927,13 +1002,13 @@
dest.writeInt(mUserId);
dest.writeString(mId);
dest.writeString(mPackageName);
- dest.writeParcelable(mActivityComponent, flags);
+ dest.writeParcelable(mActivity, flags);
dest.writeParcelable(mIcon, flags);
- dest.writeString(mTitle);
+ dest.writeCharSequence(mTitle);
dest.writeInt(mTitleResId);
- dest.writeString(mText);
+ dest.writeCharSequence(mText);
dest.writeInt(mTextResId);
- dest.writeString(mDisabledMessage);
+ dest.writeCharSequence(mDisabledMessage);
dest.writeInt(mDisabledMessageResId);
dest.writeParcelable(mIntent, flags);
@@ -991,18 +1066,43 @@
sb.append("id=");
sb.append(secure ? "***" : mId);
+ sb.append(", flags=0x");
+ sb.append(Integer.toHexString(mFlags));
+ sb.append(" [");
+ if (!isEnabled()) {
+ sb.append("X");
+ }
+ if (isImmutable()) {
+ sb.append("Im");
+ }
+ if (isManifestShortcut()) {
+ sb.append("M");
+ }
+ if (isDynamic()) {
+ sb.append("D");
+ }
+ if (isPinned()) {
+ sb.append("P");
+ }
+ if (hasIconFile()) {
+ sb.append("If");
+ }
+ if (hasIconResource()) {
+ sb.append("Ir");
+ }
+ if (hasKeyFieldsOnly()) {
+ sb.append("K");
+ }
+ if (hasStringResourcesResolved()) {
+ sb.append("Sr");
+ }
+ sb.append("]");
+
sb.append(", packageName=");
sb.append(mPackageName);
- if (isDynamic()) {
- sb.append(", dynamic");
- }
- if (isPinned()) {
- sb.append(", pinned");
- }
-
sb.append(", activity=");
- sb.append(mActivityComponent);
+ sb.append(mActivity);
sb.append(", title=");
sb.append(secure ? "***" : mTitle);
@@ -1040,35 +1140,6 @@
sb.append(", extras=");
sb.append(mExtras);
- sb.append(", flags=");
- sb.append(mFlags);
- sb.append(" [");
- if (hasFlags(FLAG_DISABLED)) {
- sb.append("X");
- }
- if (hasFlags(FLAG_FROM_MANIFEST)) {
- sb.append("M");
- }
- if (hasFlags(FLAG_DYNAMIC)) {
- sb.append("D");
- }
- if (hasFlags(FLAG_PINNED)) {
- sb.append("P");
- }
- if (hasFlags(FLAG_HAS_ICON_FILE)) {
- sb.append("If");
- }
- if (hasFlags(FLAG_HAS_ICON_RES)) {
- sb.append("Ir");
- }
- if (hasFlags(FLAG_KEY_FIELDS_ONLY)) {
- sb.append("K");
- }
- if (hasFlags(FLAG_STRINGS_RESOLVED)) {
- sb.append("S");
- }
- sb.append("]");
-
if (includeInternalData) {
sb.append(", iconRes=");
@@ -1084,16 +1155,16 @@
/** @hide */
public ShortcutInfo(
- @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
- Icon icon, String title, int titleResId, String text, int textResId,
- String disabledMessage, int disabledMessageResId, Set<String> categories, Intent intent,
- PersistableBundle intentPersistableExtras,
+ @UserIdInt int userId, String id, String packageName, ComponentName activity,
+ Icon icon, CharSequence title, int titleResId, CharSequence text, int textResId,
+ CharSequence disabledMessage, int disabledMessageResId, Set<String> categories,
+ Intent intent, PersistableBundle intentPersistableExtras,
int rank, PersistableBundle extras, long lastChangedTimestamp,
int flags, int iconResId, String bitmapPath) {
mUserId = userId;
mId = id;
mPackageName = packageName;
- mActivityComponent = activityComponent;
+ mActivity = activity;
mIcon = icon;
mTitle = title;
mTitleResId = titleResId;
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 44fa98d..3d214a84 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -28,7 +28,7 @@
// TODO Enhance javadoc
/**
- * <b>TODO: Update to reflect DR changes.</b><br>
+ * <b>TODO: Update to reflect DR changes, such as manifest shortcuts.</b><br>
*
* {@link ShortcutManager} manages shortcuts created by applications.
*
@@ -36,8 +36,8 @@
*
* An application can publish shortcuts with {@link #setDynamicShortcuts(List)} and
* {@link #addDynamicShortcuts(List)}. There can be at most
- * {@link #getMaxDynamicShortcutCount()} number of dynamic shortcuts at a time from the same
- * application.
+ * {@link #getMaxShortcutCountForActivity()} number of dynamic shortcuts at a time from the
+ * same application.
* A dynamic shortcut can be deleted with {@link #removeDynamicShortcuts(List)}, and apps
* can also use {@link #removeAllDynamicShortcuts()} to delete all dynamic shortcuts.
*
@@ -51,7 +51,8 @@
* <p>The number of pinned shortcuts does not affect the number of dynamic shortcuts that can be
* published by an application at a time.
* No matter how many pinned shortcuts that Launcher has for an application, the
- * application can still always publish {@link #getMaxDynamicShortcutCount()} number of dynamic
+ * application can still always publish {@link #getMaxShortcutCountForActivity()} number of
+ * dynamic
* shortcuts.
*
* <h3>Shortcut IDs</h3>
@@ -132,7 +133,7 @@
* @return {@code true} if the call has succeeded. {@code false} if the call is rate-limited.
*
* @throws IllegalArgumentException if {@code shortcutInfoList} contains more than
- * {@link #getMaxDynamicShortcutCount()} shortcuts.
+ * {@link #getMaxShortcutCountForActivity()} shortcuts.
*/
public boolean setDynamicShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
try {
@@ -145,7 +146,7 @@
/**
* Return all dynamic shortcuts from the caller application. The number of result items
- * will not exceed the value returned by {@link #getMaxDynamicShortcutCount()}.
+ * will not exceed the value returned by {@link #getMaxShortcutCountForActivity()}.
*/
@NonNull
public List<ShortcutInfo> getDynamicShortcuts() {
@@ -158,6 +159,19 @@
}
/**
+ * TODO Javadoc
+ */
+ @NonNull
+ public List<ShortcutInfo> getManifestShortcuts() {
+ try {
+ return mService.getManifestShortcuts(mContext.getPackageName(), injectMyUserId())
+ .getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Publish list of dynamic shortcuts. If there's already dynamic or pinned shortcuts with
* the same IDs, they will all be updated.
*
@@ -269,9 +283,21 @@
}
/**
- * Return the max number of dynamic shortcuts that each application can have at a time.
+ * TODO Javadoc
*/
- public int getMaxDynamicShortcutCount() {
+ public void enableShortcuts(@NonNull List<String> shortcutIds) {
+ try {
+ mService.enableShortcuts(mContext.getPackageName(), shortcutIds, injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the max number of dynamic shortcuts + manifest shortcuts that each launcehr icon
+ * can have at a time.
+ */
+ public int getMaxShortcutCountForActivity() {
try {
return mService.getMaxDynamicShortcutCount(mContext.getPackageName(), injectMyUserId());
} catch (RemoteException e) {
@@ -308,10 +334,23 @@
}
/**
- * Return the max width and height for icons, in pixels.
+ * Return the max width for icons, in pixels.
*/
- public int getIconMaxDimensions() {
+ public int getIconMaxWidth() {
try {
+ // TODO Implement it properly using xdpi.
+ return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the max height for icons, in pixels.
+ */
+ public int getIconMaxHeight() {
+ try {
+ // TODO Implement it properly using ydpi.
return mService.getIconMaxDimensions(mContext.getPackageName(), injectMyUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 32a2795..000751e 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -292,8 +292,10 @@
return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
AssetManager.ACCESS_STREAMING);
} catch (Exception e) {
- NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
- " from drawable resource ID #0x" + Integer.toHexString(id));
+ // Note: value.string might be null
+ NotFoundException rnf = new NotFoundException("File "
+ + (value.string == null ? "(null)" : value.string.toString())
+ + " from drawable resource ID #0x" + Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index c285acb..ce7a124 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -1021,9 +1021,19 @@
}
/**
- * Returns True if the device supports Sustained Performance Mode.
- * Applications Should check if the device supports this mode, before
- * using {@link android.view.Window#setSustainedPerformanceMode}.
+ * This function checks if the device has implemented Sustained Performance
+ * Mode. This needs to be checked only once and is constant for a particular
+ * device/release.
+ *
+ * Sustained Performance Mode is intended to provide a consistent level of
+ * performance for prolonged amount of time.
+ *
+ * Applications should check if the device supports this mode, before using
+ * {@link android.view.Window#setSustainedPerformanceMode}.
+ *
+ * @return Returns True if the device supports it, false otherwise.
+ *
+ * @see android.view.Window#setSustainedPerformanceMode
*/
public boolean isSustainedPerformanceModeSupported() {
return mContext.getResources().getBoolean(
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2613994..4b0cb09 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -789,7 +789,7 @@
*/
public boolean isPrimaryUser() {
UserInfo user = getUserInfo(UserHandle.myUserId());
- return user != null ? user.isPrimary() : false;
+ return user != null && user.isPrimary();
}
/**
@@ -855,7 +855,19 @@
*/
public boolean isGuestUser() {
UserInfo user = getUserInfo(UserHandle.myUserId());
- return user != null ? user.isGuest() : false;
+ return user != null && user.isGuest();
+ }
+
+ /**
+ * Checks if the calling app is running in a demo user.
+ * <p>
+ * Caller must hold the MANAGE_USERS permission.
+ * @return whether the caller is a demo user.
+ * @hide
+ */
+ public boolean isDemoUser() {
+ UserInfo user = getUserInfo(UserHandle.myUserId());
+ return user != null && user.isDemo();
}
/**
@@ -924,6 +936,7 @@
/** {@hide} */
public boolean isUserRunning(int userId) {
+ // TODO Switch to using UMS internal isUserRunning
try {
return ActivityManagerNative.getDefault().isUserRunning(userId, 0);
} catch (RemoteException re) {
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 551ea6b..93afb43 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -11,7 +11,7 @@
* 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.
+ * limitations under the License
*/
package android.os;
@@ -129,15 +129,23 @@
/**
* Return whether the given user is running in an
- * {@link com.android.server.am.UserState#STATE_RUNNING_UNLOCKING "unlocking"} or
- * {@link com.android.server.am.UserState#STATE_RUNNING_UNLOCKED "unlocked"} state.
+ * {@code UserState.STATE_RUNNING_UNLOCKING} or
+ * {@code UserState.STATE_RUNNING_UNLOCKED} state.
*/
public abstract boolean isUserUnlockingOrUnlocked(int userId);
/**
- * Sets whether the given user is running in an
- * {@link com.android.server.am.UserState#STATE_RUNNING_UNLOCKING "unlocking"} or
- * {@link com.android.server.am.UserState#STATE_RUNNING_UNLOCKED "unlocked"} state.
+ * Return whether the given user is running
*/
- public abstract void setUserUnlockingOrUnlocked(int userId, boolean unlockingOrUnlocked);
+ public abstract boolean isUserRunning(int userId);
+
+ /**
+ * Set user's running state
+ */
+ public abstract void setUserState(int userId, int userState);
+
+ /**
+ * Remove user's running state
+ */
+ public abstract void removeUserState(int userId);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9e8e5ef..1b4c15d 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -60,8 +60,8 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-
import android.util.MemoryIntArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.ILockSettings;
@@ -7969,6 +7969,7 @@
* idle_factor (float)
* min_time_to_alarm (long)
* max_temp_app_whitelist_duration (long)
+ * notification_whitelist_duration (long)
* </pre>
*
* <p>
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 1c13962..b8fd2ff 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -67,7 +67,8 @@
private static final String TAG = "TextUtils";
/* package */ static final char[] ELLIPSIS_NORMAL = { '\u2026' }; // this is "..."
- private static final String ELLIPSIS_STRING = new String(ELLIPSIS_NORMAL);
+ /** {@hide} */
+ public static final String ELLIPSIS_STRING = new String(ELLIPSIS_NORMAL);
/* package */ static final char[] ELLIPSIS_TWO_DOTS = { '\u2025' }; // this is ".."
private static final String ELLIPSIS_TWO_DOTS_STRING = new String(ELLIPSIS_TWO_DOTS);
diff --git a/core/java/android/view/inputmethod/InputMethodManagerInternal.java b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
index ce9908c..77df4e3 100644
--- a/core/java/android/view/inputmethod/InputMethodManagerInternal.java
+++ b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
@@ -32,4 +32,9 @@
* Called by the window manager to let the input method manager rotate the input method.
*/
void switchInputMethod(boolean forwardDirection);
+
+ /**
+ * Hides the current input method, if visible.
+ */
+ void hideCurrentInputMethod();
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3711b94..5cbd284 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6857,11 +6857,11 @@
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy)
- .setHyphenationFrequency(mHyphenationFrequency)
- .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
+ .setHyphenationFrequency(mHyphenationFrequency);
if (shouldEllipsize) {
builder.setEllipsize(mEllipsize)
- .setEllipsizedWidth(ellipsisWidth);
+ .setEllipsizedWidth(ellipsisWidth)
+ .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
}
mHintLayout = builder.build();
}
@@ -6948,12 +6948,11 @@
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy)
- .setHyphenationFrequency(mHyphenationFrequency)
- .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
+ .setHyphenationFrequency(mHyphenationFrequency);
if (shouldEllipsize) {
builder.setEllipsize(effectiveEllipsize)
- .setEllipsizedWidth(ellipsisWidth);
-
+ .setEllipsizedWidth(ellipsisWidth)
+ .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
}
// TODO: explore always setting maxLines
result = builder.build();
@@ -6983,19 +6982,14 @@
return false;
}
- private static int desired(Layout layout, int maxLines) {
+ private static int desired(Layout layout) {
int n = layout.getLineCount();
CharSequence text = layout.getText();
float max = 0;
- // if maxLines is set, and the text length is greater that the length of the text in the
- // layout, it means that there is a cut-off and we cannot use it.
- if (maxLines != -1 && text.length() > layout.getLineEnd(n - 1)) {
- return -1;
- }
-
// if any line was wrapped, we can't use it.
// but it's ok for the last line not to have a newline
+
for (int i = 0; i < n - 1; i++) {
if (text.charAt(layout.getLineEnd(i) - 1) != '\n')
return -1;
@@ -7068,7 +7062,7 @@
width = widthSize;
} else {
if (mLayout != null && mEllipsize == null) {
- des = desired(mLayout, getMaxLines());
+ des = desired(mLayout);
}
if (des < 0) {
@@ -7100,7 +7094,7 @@
int hintWidth;
if (mHintLayout != null && mEllipsize == null) {
- hintDes = desired(mHintLayout, getMaxLines());
+ hintDes = desired(mHintLayout);
}
if (hintDes < 0) {
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index 106999b..d26be91 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -16,9 +16,11 @@
package com.android.internal.app;
+import android.annotation.IntRange;
import android.icu.text.ListFormatter;
import android.icu.util.ULocale;
import android.os.LocaleList;
+import android.text.TextUtils;
import java.text.Collator;
import java.util.Comparator;
@@ -153,16 +155,34 @@
* @param locales the list of locales whose names is to be displayed.
* @param displayLocale the locale in which to display the names.
* If this is null, it will use the default locale.
+ * @param maxLocales maximum number of locales to display. Generates ellipsis after that.
* @return the locale aware list of locale names
*/
- public static String getDisplayLocaleList(LocaleList locales, Locale displayLocale) {
+ public static String getDisplayLocaleList(
+ LocaleList locales, Locale displayLocale, @IntRange(from=1) int maxLocales) {
+
final Locale dispLocale = displayLocale == null ? Locale.getDefault() : displayLocale;
- int localeCount = locales.size();
- final String[] localeNames = new String[localeCount];
+ final boolean ellipsisNeeded = locales.size() > maxLocales;
+ final int localeCount, listCount;
+ if (ellipsisNeeded) {
+ localeCount = maxLocales;
+ listCount = maxLocales + 1; // One extra slot for the ellipsis
+ } else {
+ listCount = localeCount = locales.size();
+ }
+ final String[] localeNames = new String[listCount];
for (int i = 0; i < localeCount; i++) {
localeNames[i] = LocaleHelper.getDisplayName(locales.get(i), dispLocale, false);
}
+ if (ellipsisNeeded) {
+ // Theoretically, we want to extract this from ICU's Resource Bundle for
+ // "Ellipsis/final", which seems to have different strings than the normal ellipsis for
+ // Hong Kong Traditional Chinese (zh_Hant_HK) and Dzongkha (dz). But that has two
+ // problems: it's expensive to extract it, and in case the output string becomes
+ // automatically ellipsized, it can result in weird output.
+ localeNames[maxLocales] = TextUtils.ELLIPSIS_STRING;
+ }
ListFormatter lfn = ListFormatter.getInstance(dispLocale);
return lfn.format((Object[]) localeNames);
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index 9e69f79..045f127 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -36,6 +36,13 @@
return reinterpret_cast<jlong>(tree);
}
+static jlong createTreeFromCopy(JNIEnv*, jobject, jlong treePtr, jlong groupPtr) {
+ VectorDrawable::Group* rootGroup = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ VectorDrawable::Tree* treeToCopy = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ VectorDrawable::Tree* tree = new VectorDrawable::Tree(treeToCopy, rootGroup);
+ return reinterpret_cast<jlong>(tree);
+}
+
static jlong createEmptyFullPath(JNIEnv*, jobject) {
VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath();
return reinterpret_cast<jlong>(newPath);
@@ -344,6 +351,7 @@
static const JNINativeMethod gMethods[] = {
{"nCreateTree", "!(J)J", (void*)createTree},
+ {"nCreateTreeFromCopy", "!(JJ)J", (void*)createTreeFromCopy},
{"nSetRendererViewportSize", "!(JFF)V", (void*)setTreeViewportSize},
{"nSetRootAlpha", "!(JF)Z", (void*)setRootAlpha},
{"nGetRootAlpha", "!(J)F", (void*)getRootAlpha},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6c289dc..b2028a7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -481,6 +481,8 @@
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_AVAILABLE" />
<protected-broadcast android:name="android.intent.action.MANAGED_PROFILE_UNAVAILABLE" />
+ <protected-broadcast android:name="com.android.server.am.ACTION_RESET_DEMO" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml
index 7147ea2..9a5bbd4 100644
--- a/core/res/res/layout/app_error_dialog.xml
+++ b/core/res/res/layout/app_error_dialog.xml
@@ -35,11 +35,11 @@
/>
<Button
- android:id="@+id/aerr_reset"
+ android:id="@+id/aerr_close"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/aerr_reset"
- android:drawableStart="@drawable/ic_refresh"
+ android:text="@string/aerr_close"
+ android:drawableStart="@drawable/ic_close"
style="@style/aerr_list_item"
/>
@@ -53,15 +53,6 @@
/>
<Button
- android:id="@+id/aerr_close"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/aerr_close"
- android:drawableStart="@drawable/ic_close"
- style="@style/aerr_list_item"
- />
-
- <Button
android:id="@+id/aerr_mute"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index d16dcc6..38ea92a 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -96,6 +96,7 @@
android:layout_height="wrap_content"
android:paddingTop="1dp"
android:visibility="gone"
+ android:contentDescription="@string/expand_button_content_description"
/>
<ImageView android:id="@+id/profile_badge"
android:layout_width="@dimen/notification_badge_size"
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index fc53a1a..c43975e 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -33,8 +33,6 @@
style="?attr/preferenceFragmentListStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingTop="0dip"
- android:paddingBottom="@dimen/preference_fragment_padding_bottom"
android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
android:clipToPadding="false"
android:drawSelectorOnTop="false"
diff --git a/core/res/res/layout/preference_list_fragment_material.xml b/core/res/res/layout/preference_list_fragment_material.xml
index e411c0e..db2fe7d 100644
--- a/core/res/res/layout/preference_list_fragment_material.xml
+++ b/core/res/res/layout/preference_list_fragment_material.xml
@@ -32,8 +32,6 @@
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingTop="0dip"
- android:paddingBottom="@dimen/preference_fragment_padding_bottom"
style="?attr/preferenceFragmentListStyle"
android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
android:clipToPadding="false"
diff --git a/core/res/res/values-notround/styles_micro.xml b/core/res/res/values-notround/styles_micro.xml
index c45e36459..1294660 100644
--- a/core/res/res/values-notround/styles_micro.xml
+++ b/core/res/res/values-notround/styles_micro.xml
@@ -17,4 +17,6 @@
<style name="TextAppearance.Micro.AlertDialog.Message" parent="TextAppearance.Micro.Small">
<item name="textAlignment">viewStart</item>
</style>
+ <style name="PreferenceFragment.Micro" parent="PreferenceFragment.Material"/>
+ <style name="PreferenceFragmentList.Micro" parent="PreferenceFragmentList.Material"/>
</resources>
diff --git a/core/res/res/values-round/styles_micro.xml b/core/res/res/values-round/styles_micro.xml
index 7a1e511..f19adde 100644
--- a/core/res/res/values-round/styles_micro.xml
+++ b/core/res/res/values-round/styles_micro.xml
@@ -17,4 +17,13 @@
<style name="TextAppearance.Micro.AlertDialog.Message" parent="TextAppearance.Micro.Small">
<item name="textAlignment">center</item>
</style>
+ <style name="PreferenceFragment.Micro" parent="PreferenceFragment.Material">
+ <item name="paddingTop">22dp</item>
+ <item name="paddingBottom">22dp</item>
+ <item name="clipToPadding">false</item>
+ </style>
+ <style name="PreferenceFragmentList.Micro" parent="PreferenceFragmentList.Material">
+ <item name="paddingTop">22dp</item>
+ <item name="paddingBottom">22dp</item>
+ </style>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e0fbca1..88c5629 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -593,6 +593,9 @@
the appearance matches the branding of the app requesting the fingerprint scan.-->
<attr name="fingerprintAuthDrawable" format="reference" />
+ <!-- Asset that should be used to show users the position of the NFC antenna on the device. -->
+ <attr name="nfcAntennaPositionDrawable" format="reference" />
+
<!-- ============ -->
<!-- Panel styles -->
<!-- ============ -->
@@ -4602,6 +4605,10 @@
screens with limited space for text. -->
<enum name="full" value="2" />
</attr>
+ <!-- Placeholder for a deleted attribute. This should be removed before M release. -->
+ <attr name="__removeBeforeMRelease_leftIndents" format="reference" />
+ <!-- Placeholder for a deleted attribute. This should be removed before M release. -->
+ <attr name="__removeBeforeMRelease_rightIndents" format="reference" />
</declare-styleable>
<declare-styleable name="TextViewAppearance">
<!-- Base text color, typeface, size, and style. -->
@@ -8237,4 +8244,17 @@
color. -->
<attr name="colorBackground" />
</declare-styleable>
+
+ <declare-styleable name="Shortcut">
+ <attr name="shortcutId" format="string" />
+ <attr name="enabled" />
+ <attr name="shortcutRank" format="integer" />
+ <attr name="shortcutIcon" format="reference" />
+ <attr name="shortcutTitle" format="reference" />
+ <attr name="shortcutText" format="reference" />
+ <attr name="shortcutDisabledMessage" format="reference" />
+ <attr name="shortcutCategories" format="string" />
+ <attr name="shortcutIntentAction" format="string" />
+ <attr name="shortcutIntentData" format="string" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6797799..421bc2a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2595,13 +2595,13 @@
<public type="attr" name="accessibilityTraversalAfter" id="0x010104d2" />
<public type="attr" name="dialogPreferredPadding" id="0x010104d3" />
<public type="attr" name="searchHintIcon" id="0x010104d4" />
- <public type="attr" name="revisionCode" id="0x010104d5" />
- <public type="attr" name="drawableTint" id="0x010104d6" />
- <public type="attr" name="drawableTintMode" id="0x010104d7" />
- <public type="attr" name="fraction" id="0x010104d8" />
+ <public type="attr" name="revisionCode" />
+ <public type="attr" name="drawableTint" />
+ <public type="attr" name="drawableTintMode" />
+ <public type="attr" name="fraction" />
- <public type="style" name="Theme.DeviceDefault.Dialog.Alert" id="0x010302d1" />
- <public type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" id="0x010302d2" />
+ <public type="style" name="Theme.DeviceDefault.Dialog.Alert" />
+ <public type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" />
<!-- ===============================================================
Resources added in version M of the platform
@@ -2653,79 +2653,98 @@
<public type="id" name="accessibilityActionScrollRight" id="0x0102003b" />
<public type="id" name="accessibilityActionContextClick" id="0x0102003c" />
- <public type="string" name="fingerprint_icon_content_description" id="0x01040018" />
+ <public type="string" name="fingerprint_icon_content_description" />
<!-- ===============================================================
Resources added in version N of the platform
=============================================================== -->
<eat-comment />
- <public type="attr" name="listMenuViewStyle" id="0x010104f2" />
- <public type="attr" name="subMenuArrow" id="0x010104f3" />
- <public type="attr" name="defaultWidth" id="0x010104f4" />
- <public type="attr" name="defaultHeight" id="0x010104f5" />
- <public type="attr" name="resizeableActivity" id="0x010104f6" />
- <public type="attr" name="supportsPictureInPicture" id="0x010104f7" />
- <public type="attr" name="titleMargin" id="0x010104f8" />
- <public type="attr" name="titleMarginStart" id="0x010104f9" />
- <public type="attr" name="titleMarginEnd" id="0x010104fa" />
- <public type="attr" name="titleMarginTop" id="0x010104fb" />
- <public type="attr" name="titleMarginBottom" id="0x010104fc" />
- <public type="attr" name="maxButtonHeight" id="0x010104fd" />
- <public type="attr" name="buttonGravity" id="0x010104fe" />
- <public type="attr" name="collapseIcon" id="0x010104ff" />
- <public type="attr" name="level" id="0x01010500" />
- <public type="attr" name="contextPopupMenuStyle" id="0x01010501" />
- <public type="attr" name="textAppearancePopupMenuHeader" id="0x01010502" />
- <public type="attr" name="windowBackgroundFallback" id="0x01010503" />
- <public type="attr" name="defaultToDeviceProtectedStorage" id="0x01010504" />
- <public type="attr" name="directBootAware" id="0x01010505" />
- <public type="attr" name="preferenceFragmentStyle" id="0x01010506" />
- <public type="attr" name="canControlMagnification" id="0x01010507" />
- <public type="attr" name="languageTag" id="0x01010508" />
- <public type="attr" name="pointerIcon" id="0x01010509" />
- <public type="attr" name="tickMark" id="0x0101050a" />
- <public type="attr" name="tickMarkTint" id="0x0101050b" />
- <public type="attr" name="tickMarkTintMode" id="0x0101050c" />
- <public type="attr" name="canPerformGestures" id="0x0101050d" />
- <public type="attr" name="externalService" id="0x0101050e" />
- <public type="attr" name="supportsLocalInteraction" id="0x0101050f" />
- <public type="attr" name="startX" id="0x01010510" />
- <public type="attr" name="startY" id="0x01010511" />
- <public type="attr" name="endX" id="0x01010512" />
- <public type="attr" name="endY" id="0x01010513" />
- <public type="attr" name="offset" id="0x01010514" />
- <public type="attr" name="use32bitAbi" id="0x01010515" />
- <public type="attr" name="bitmap" id="0x01010516" />
- <public type="attr" name="hotSpotX" id="0x01010517" />
- <public type="attr" name="hotSpotY" id="0x01010518" />
- <public type="attr" name="version" id="0x01010519" />
- <public type="attr" name="backupInForeground" id="0x0101051a" />
- <public type="attr" name="countDown" id="0x0101051b" />
- <public type="attr" name="canRecord" id="0x0101051c" />
- <public type="attr" name="tunerCount" id="0x0101051d" />
- <public type="attr" name="fillType" id="0x0101051e" />
- <public type="attr" name="popupEnterTransition" id="0x0101051f" />
- <public type="attr" name="popupExitTransition" id="0x01010520" />
- <public type="attr" name="forceHasOverlappingRendering" id="0x01010521" />
- <public type="attr" name="contentInsetStartWithNavigation" id="0x01010522" />
- <public type="attr" name="contentInsetEndWithActions" id="0x01010523" />
- <public type="attr" name="numberPickerStyle" id="0x01010524" />
- <public type="attr" name="enableVrMode" id="0x01010525" />
- <public type="attr" name="hash" id="0x01010526" />
- <public type="attr" name="networkSecurityConfig" id="0x01010527" />
+ <public type="attr" name="listMenuViewStyle" />
+ <public type="attr" name="subMenuArrow" />
+ <public type="attr" name="defaultWidth" />
+ <public type="attr" name="defaultHeight" />
+ <!-- @removed -->
+ <public type="attr" name="minimalWidth" />
+ <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" />
+ <public type="attr" name="titleMarginTop" />
+ <public type="attr" name="titleMarginBottom" />
+ <public type="attr" name="maxButtonHeight" />
+ <public type="attr" name="buttonGravity" />
+ <public type="attr" name="collapseIcon" />
+ <public type="attr" name="level" />
+ <public type="attr" name="contextPopupMenuStyle" />
+ <public type="attr" name="textAppearancePopupMenuHeader" />
+ <public type="attr" name="windowBackgroundFallback" />
+ <public type="attr" name="defaultToDeviceProtectedStorage" />
+ <public type="attr" name="directBootAware" />
+ <public type="attr" name="preferenceFragmentStyle" />
+ <public type="attr" name="canControlMagnification" />
+ <public type="attr" name="languageTag" />
+ <public type="attr" name="pointerIcon" />
+ <public type="attr" name="tickMark" />
+ <public type="attr" name="tickMarkTint" />
+ <public type="attr" name="tickMarkTintMode" />
+ <public type="attr" name="canPerformGestures" />
+ <public type="attr" name="externalService" />
+ <public type="attr" name="supportsLocalInteraction" />
+ <public type="attr" name="startX" />
+ <public type="attr" name="startY" />
+ <public type="attr" name="endX" />
+ <public type="attr" name="endY" />
+ <public type="attr" name="offset" />
+ <public type="attr" name="use32bitAbi" />
+ <public type="attr" name="bitmap" />
+ <public type="attr" name="hotSpotX" />
+ <public type="attr" name="hotSpotY" />
+ <public type="attr" name="version" />
+ <public type="attr" name="backupInForeground" />
+ <public type="attr" name="countDown" />
+ <public type="attr" name="canRecord" />
+ <public type="attr" name="tunerCount" />
+ <public type="attr" name="nfcAntennaPositionDrawable" />
+ <public type="attr" name="fillType" />
+ <public type="attr" name="popupEnterTransition" />
+ <public type="attr" name="popupExitTransition" />
+ <!-- @removed -->
+ <public type="attr" name="minimalHeight" />
+ <public type="attr" name="forceHasOverlappingRendering" />
+ <public type="attr" name="contentInsetStartWithNavigation" />
+ <public type="attr" name="contentInsetEndWithActions" />
+ <public type="attr" name="numberPickerStyle" />
+ <public type="attr" name="enableVrMode" />
+ <public type="attr" name="hash" />
+ <public type="attr" name="networkSecurityConfig" />
- <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" id="0x010302d8" />
- <public type="style" name="Widget.Material.SeekBar.Discrete" id="0x010302d9" />
- <public type="style" name="Widget.Material.CompoundButton.Switch" id="0x010302da" />
- <public type="style" name="Widget.Material.Light.CompoundButton.Switch" id="0x010302db" />
- <public type="style" name="Widget.Material.NumberPicker" id="0x010302dc" />
- <public type="style" name="Widget.Material.Light.NumberPicker" id="0x010302dd" />
- <public type="style" name="TextAppearance.Material.Widget.Button.Colored" id="0x010302de" />
- <public type="style" name="TextAppearance.Material.Widget.Button.Borderless.Colored" id="0x010302df" />
+ <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
+ <public type="style" name="Widget.Material.SeekBar.Discrete" />
+ <public type="style" name="Widget.Material.CompoundButton.Switch" />
+ <public type="style" name="Widget.Material.Light.CompoundButton.Switch" />
+ <public type="style" name="Widget.Material.NumberPicker" />
+ <public type="style" name="Widget.Material.Light.NumberPicker" />
+ <public type="style" name="TextAppearance.Material.Widget.Button.Colored" />
+ <public type="style" name="TextAppearance.Material.Widget.Button.Borderless.Colored" />
- <public type="id" name="accessibilityActionSetProgress" id="0x0102003d" />
- <public type="id" name="icon_frame" id="0x0102003e" />
- <public type="id" name="list_container" id="0x0102003f" />
- <public type="id" name="switch_widget" id="0x01020040" />
+ <public type="id" name="accessibilityActionSetProgress" />
+ <public type="id" name="icon_frame" />
+ <public type="id" name="list_container" />
+ <public type="id" name="switch_widget" />
+
+ <!-- ===============================================================
+ Resources added in version N MR1 of the platform
+ =============================================================== -->
+ <eat-comment />
+ <public type="attr" name="shortcutId" />
+ <public type="attr" name="shortcutRank" />
+ <public type="attr" name="shortcutIcon" />
+ <public type="attr" name="shortcutTitle" />
+ <public type="attr" name="shortcutText" />
+ <public type="attr" name="shortcutDisabledMessage" />
+ <public type="attr" name="shortcutCategories" />
+ <public type="attr" name="shortcutIntentAction" />
+ <public type="attr" name="shortcutIntentData" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e30fd1a..eef8fcf 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2725,9 +2725,7 @@
<!-- Text of the alert that is displayed when a process has crashed repeatedly. -->
<string name="aerr_process_repeated"><xliff:g id="process">%1$s</xliff:g> keeps stopping</string>
<!-- Button that restarts a crashed application -->
- <string name="aerr_restart">Restart app</string>
- <!-- Button that clears cache and restarts a crashed application -->
- <string name="aerr_reset">Reset and restart app</string>
+ <string name="aerr_restart">Open app again</string>
<!-- Button that sends feedback about a crashed application -->
<string name="aerr_report">Send feedback</string>
<!-- Button that closes a crashed application -->
@@ -4261,6 +4259,12 @@
<!-- Content description of the work profile icon in the notification. -->
<string name="notification_work_profile_content_description">Work profile</string>
+ <!-- Content description of the expand button icon in the notification.-->
+ <string name="expand_button_content_description">Expand button</string>
+
+ <!-- Accessibility action description on the expand button. -->
+ <string name="expand_action_accessibility">Click to expand</string>
+
<!-- User visible name for USB MIDI Peripheral port -->
<string name="usb_midi_peripheral_name">Android USB Peripheral Port</string>
<!-- Manufacturer name for USB MIDI Peripheral port -->
@@ -4358,6 +4362,11 @@
<!-- The representation of a time duration when negative. An example is -1:14. This can be used with a countdown timer for example.-->
<string name="negative_duration">\u2212<xliff:g id="time" example="1:14">%1$s</xliff:g></string>
+ <!-- Title of notification to start a new demo session when device is in retail mode [CHAR LIMIT=NONE] -->
+ <string name="reset_retail_demo_mode_title">Restart Session</string>
+ <!-- Text of notification to start a new demo session when device is in retail mode [CHAR LIMIT=NONE] -->
+ <string name="reset_retail_demo_mode_text">Tap to start a new demo session</string>
+
<!-- Title of notification shown when device has been forced to safe mode after a security compromise. -->
<string name="audit_safemode_notification">Factory reset to use this device without restrictions</string>
<!-- Description of notification shown when device has been forced to safe mode after a security compromise. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 790dcfa..273086d 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1122,6 +1122,8 @@
<style name="PreferenceFragmentList">
<item name="paddingStart">@dimen/preference_fragment_padding_side</item>
<item name="paddingEnd">@dimen/preference_fragment_padding_side</item>
+ <item name="paddingTop">0dp</item>
+ <item name="paddingBottom">@dimen/preference_fragment_padding_bottom</item>
</style>
<!-- Other Misc Styles -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 115c49f..bd51258 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1888,6 +1888,8 @@
<java-symbol type="string" name="config_persistentDataPackageName" />
<java-symbol type="string" name="audit_safemode_notification" />
<java-symbol type="string" name="audit_safemode_notification_details" />
+ <java-symbol type="string" name="reset_retail_demo_mode_title" />
+ <java-symbol type="string" name="reset_retail_demo_mode_text" />
<java-symbol type="layout" name="resolver_list" />
<java-symbol type="id" name="resolver_list" />
@@ -2446,7 +2448,6 @@
<java-symbol type="id" name="work_widget_badge_icon" />
<java-symbol type="id" name="aerr_report" />
- <java-symbol type="id" name="aerr_reset" />
<java-symbol type="id" name="aerr_restart" />
<java-symbol type="id" name="aerr_close" />
<java-symbol type="id" name="aerr_mute" />
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 89c33b2..63a6a484 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -52,6 +52,10 @@
<item name="colorPrimary">@color/primary_micro_dark</item>
<item name="colorAccent">@color/accent_micro_dark</item>
<item name="colorEdgeEffect">?attr/colorPrimary</item>
+
+ <!-- Preference styles -->
+ <item name="preferenceFragmentStyle">@style/PreferenceFragment.Micro</item>
+ <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList.Micro</item>
</style>
<style name="Theme.Micro" parent="Theme.MicroBase">
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 9e9343e..2464e9e 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -50,25 +50,25 @@
- from: /tools/sdk/installing.html
to: /studio/index.html
- from: /tools/eclipse/installing-adt.html
- to: /studio/tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
- from: /sdk/requirements.html
to: /studio/index.html
- from: /sdk/installing/next.html
to: /training/basics/firstapp/index.html
- from: /sdk/installing/installing-adt.html
- to: /studio/tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
- from: /tools/projects/projects-eclipse.html
- to: /studio/tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
- from: /tools/building/building-eclipse.html
- to: /studio/tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
- from: /tools/building/building-cmdline-ant.html
- to: /studio/tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
- from: /tools/testing/testing_eclipse.html
- to: /studio/tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
- from: /tools/debugging/debugging-projects.html
- to: /studio/tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
- from: /tools/publishing/app-signing-eclipse.html
- to: /studio/tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
- from: /tools/testing/testing_ui.html
to: /training/testing/ui-testing/index.html
- from: /tools/testing/activity_test.html
@@ -99,10 +99,6 @@
to: /training/testing/ui-testing/index.html
- from: /guide/market/
to: /google/play/
-- from: /guide/google/gcm/client-javadoc/...
- to: /reference/com/google/android/gms/gcm/package-summary.html
-- from: /guide/google/gcm/server-javadoc/...
- to: /reference/com/google/android/gms/gcm/package-summary.html
- from: /guide/google/play/services.html
to: https://developers.google.com/android/guides/overview
- from: /guide/google/
@@ -771,10 +767,6 @@
to: https://developers.google.com/android/guides/api-client
- from: /google/auth/http-auth.html
to: https://developers.google.com/android/guides/http-auth
-- from: /reference/gms-packages.html
- to: https://developers.google.com/android/reference/packages
-- from: /reference/com/google/android/gms/
- to: https://developers.google.com/android/reference/com/google/android/gms/
- from: /preview/features/runtime-permissions.html
to: /training/permissions/index.html
- from: /preview/features/app-linking.html
@@ -865,11 +857,18 @@
- from: /tools/testing-support-library/...
to: /topic/libraries/testing-support-library/...
-# GCM redirects
+# GMS and GCM reference redirects
+- from: /reference/gms-packages.html
+ to: https://developers.google.com/android/reference/packages
+- from: /guide/google/gcm/client-javadoc/...
+ to: https://developers.google.com/android/reference/com/google/android/gms/gcm/package-summary
+- from: /guide/google/gcm/server-javadoc/...
+ to: https://developers.google.com/android/reference/com/google/android/gms/gcm/package-summary
+- from: /reference/com/google/android/gcm/...
+ to: https://developers.google.com/android/reference/com/google/android/gms/gcm/package-summary
- from: /reference/com/google/...
to: https://developers.google.com/android/reference/com/google/...
-
# Files moved during the /studio restructure
- from: /sdk/index.html
to: /studio/index.html
@@ -1147,6 +1146,8 @@
to: /studio/profile/ddms.html
- from: /tools/revisions/index.html
to: /studio/releases/index.html
+- from: /tools/help/adt.html
+ to: /studio/tools/sdk/eclipse-adt.html
# Just incase something was missed, go to intro page
- from: /tools/...
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index f4a03db..fff2608 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -63,19 +63,10 @@
<br/>Any versions with less than 0.1% distribution are not shown.</em>
</p>
-<p class="note"><strong>Note:</strong> This data is gathered from the new Google Play
-Store app, which supports Android 2.2 and above, so devices running older versions are not included.
-However, in August, 2013, versions older than Android 2.2 accounted for about 1% of devices that
-<em>checked in</em> to Google servers (not those that actually visited Google Play Store).
-</p>
-
-
-
<h2 id="Screens">Screen Sizes and Densities</h2>
-
<p>This section provides data about the relative number of devices that have a particular
screen configuration, defined by a combination of screen size and density. To simplify the way that
you design your user interfaces for different screen configurations, Android divides the range of
diff --git a/docs/html/guide/topics/providers/content-provider-creating.jd b/docs/html/guide/topics/providers/content-provider-creating.jd
index 7cd3d69..baa92e1 100755
--- a/docs/html/guide/topics/providers/content-provider-creating.jd
+++ b/docs/html/guide/topics/providers/content-provider-creating.jd
@@ -409,25 +409,27 @@
public class ExampleProvider extends ContentProvider {
...
// Creates a UriMatcher object.
- private static final UriMatcher sUriMatcher;
-...
- /*
- * The calls to addURI() go here, for all of the content URI patterns that the provider
- * should recognize. For this snippet, only the calls for table 3 are shown.
- */
-...
- /*
- * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
- * in the path
- */
- sUriMatcher.addURI("com.example.app.provider", "table3", 1);
+ private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- /*
- * Sets the code for a single row to 2. In this case, the "#" wildcard is
- * used. "content://com.example.app.provider/table3/3" matches, but
- * "content://com.example.app.provider/table3 doesn't.
- */
- sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
+ static {
+ /*
+ * The calls to addURI() go here, for all of the content URI patterns that the provider
+ * should recognize. For this snippet, only the calls for table 3 are shown.
+ */
+
+ /*
+ * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
+ * in the path
+ */
+ sUriMatcher.addURI("com.example.app.provider", "table3", 1);
+
+ /*
+ * Sets the code for a single row to 2. In this case, the "#" wildcard is
+ * used. "content://com.example.app.provider/table3/3" matches, but
+ * "content://com.example.app.provider/table3 doesn't.
+ */
+ sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
+ }
...
// Implements ContentProvider.query()
public Cursor query(
diff --git a/docs/html/ndk/guides/audio/opensl-prog-notes.jd b/docs/html/ndk/guides/audio/opensl-prog-notes.jd
index 3263145..e70aa08 100644
--- a/docs/html/ndk/guides/audio/opensl-prog-notes.jd
+++ b/docs/html/ndk/guides/audio/opensl-prog-notes.jd
@@ -61,8 +61,8 @@
create array and can be added
later after the object is created. The Android implementation provides
a convenience feature to
-avoid this complexity; see the
- <a href="#dynamic-interfaces">Dynamic interfaces at object creation</a> section above.
+avoid this complexity, which is described in
+ <a href="{@docRoot}ndk/guides/audio/opensl-for-android.html#dynamic-interfaces">Dynamic interfaces at object creation</a>.
</p>
<p>
diff --git a/docs/html/samples/index.jd b/docs/html/samples/index.jd
index 76ea573..5885086 100644
--- a/docs/html/samples/index.jd
+++ b/docs/html/samples/index.jd
@@ -35,9 +35,8 @@
from GitHub as a new project.
</p>
-<p>For more information about importing samples, see
-<a href="{@docRoot}tools/studio/index.html#git-samples">Easy access to Android code samples on
-GitHub</a>.</p>
+<p>To view the Android code samples that you can import, see the
+<a class="external-link" href="https://github.com/googlesamples/">Google Samples page</a> on GitHub.</p>
<h2>Download Samples</h2>
diff --git a/docs/html/training/sync-adapters/running-sync-adapter.jd b/docs/html/training/sync-adapters/running-sync-adapter.jd
index 033450f..a640aa6 100644
--- a/docs/html/training/sync-adapters/running-sync-adapter.jd
+++ b/docs/html/training/sync-adapters/running-sync-adapter.jd
@@ -240,6 +240,7 @@
* Ask the framework to run your sync adapter.
* To maintain backward compatibility, assume that
* changeUri is null.
+ */
ContentResolver.requestSync(ACCOUNT, AUTHORITY, null);
}
...
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index dd33e98..f5592fa 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -804,7 +804,7 @@
mTintMode = copy.mTintMode;
mAutoMirrored = copy.mAutoMirrored;
mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
- createNativeTree(mRootGroup);
+ createNativeTreeFromCopy(copy, mRootGroup);
mBaseWidth = copy.mBaseWidth;
mBaseHeight = copy.mBaseHeight;
@@ -826,6 +826,16 @@
VMRuntime.getRuntime().registerNativeAllocation(NATIVE_ALLOCATION_SIZE);
}
+ // Create a new native tree with the given root group, and copy the properties from the
+ // given VectorDrawableState's native tree.
+ private void createNativeTreeFromCopy(VectorDrawableState copy, VGroup rootGroup) {
+ mNativeTree = new VirtualRefBasePtr(nCreateTreeFromCopy(
+ copy.mNativeTree.get(), rootGroup.mNativePtr));
+ // Register tree size
+ VMRuntime.getRuntime().registerNativeAllocation(NATIVE_ALLOCATION_SIZE);
+ }
+
+
void onTreeConstructionFinished() {
mRootGroup.setTree(mNativeTree);
mAllocationOfAllNodes = mRootGroup.getNativeSize();
@@ -1777,6 +1787,7 @@
}
private static native long nCreateTree(long rootGroupPtr);
+ private static native long nCreateTreeFromCopy(long treeToCopy, long rootGroupPtr);
private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
float viewportHeight);
private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index f0348e4..2b79941 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -530,7 +530,7 @@
if (prop->getRootAlpha() == 1.0f && prop->getColorFilter() == nullptr) {
return nullptr;
} else {
- outPaint->setColorFilter(mStagingProperties.getColorFilter());
+ outPaint->setColorFilter(prop->getColorFilter());
outPaint->setFilterQuality(kLow_SkFilterQuality);
outPaint->setAlpha(prop->getRootAlpha() * 255);
return outPaint;
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index b33f26c..a5d1d4b 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -542,6 +542,12 @@
Tree(Group* rootNode) : mRootNode(rootNode) {
mRootNode->setPropertyChangedListener(&mPropertyChangedListener);
}
+
+ // Copy properties from the tree and use the give node as the root node
+ Tree(const Tree* copy, Group* rootNode) : Tree(rootNode) {
+ mStagingProperties.syncAnimatableProperties(*copy->stagingProperties());
+ mStagingProperties.syncNonAnimatableProperties(*copy->stagingProperties());
+ }
// Draws the VD onto a bitmap cache, then the bitmap cache will be rendered onto the input
// canvas. Returns the number of pixels needed for the bitmap cache.
int draw(Canvas* outCanvas, SkColorFilter* colorFilter,
@@ -666,7 +672,7 @@
};
void onPropertyChanged(TreeProperties* prop);
TreeProperties* mutateStagingProperties() { return &mStagingProperties; }
- const TreeProperties* stagingProperties() { return &mStagingProperties; }
+ const TreeProperties* stagingProperties() const { return &mStagingProperties; }
PushStagingFunctor* getFunctor() { return &mFunctor;}
// This should only be called from animations on RT
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
index 423c3b0..a8b782e8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/Camera2SurfaceViewTestCase.java
@@ -37,6 +37,7 @@
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
@@ -127,6 +128,7 @@
protected List<Size> mOrderedRAW10Sizes; // In descending order.
protected List<Size> mOrderedYUV420888Sizes; // In descending order.
protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
+ protected boolean mSupportRAW10;
protected WindowManager mWindowManager;
@@ -603,16 +605,21 @@
mCamera = CameraTestUtils.openCamera(
mCameraManager, cameraId, mCameraListener, mHandler);
mCollector.setCameraId(cameraId);
- mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
- CheckLevel.ASSERT, /*collector*/null);
+ CameraCharacteristics properties = mCameraManager.getCameraCharacteristics(cameraId);
+ mStaticInfo = new StaticMetadata(properties, CheckLevel.ASSERT, /*collector*/null);
+ StreamConfigurationMap configMap =
+ properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ mSupportRAW10 = configMap.isOutputSupportedFor(ImageFormat.RAW10);
if (mStaticInfo.isColorOutputSupported()) {
mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
mOrderedVideoSizes = getSupportedVideoSizes(
cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
- mOrderedRAW10Sizes = getSortedSizesForFormat(
- cameraId, mCameraManager, ImageFormat.RAW10, null);
+ if (mSupportRAW10) {
+ mOrderedRAW10Sizes = getSortedSizesForFormat(
+ cameraId, mCameraManager, ImageFormat.RAW10, null);
+ }
mOrderedYUV420888Sizes = getSortedSizesForFormat(
cameraId, mCameraManager, ImageFormat.YUV_420_888, null);
// Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
@@ -636,6 +643,7 @@
mOrderedPreviewSizes = null;
mOrderedVideoSizes = null;
mOrderedStillSizes = null;
+ mSupportRAW10 = false;
}
}
diff --git a/packages/PrintSpooler/res/values-my-rMM/strings.xml b/packages/PrintSpooler/res/values-my-rMM/strings.xml
index b51336e..c277c55 100644
--- a/packages/PrintSpooler/res/values-my-rMM/strings.xml
+++ b/packages/PrintSpooler/res/values-my-rMM/strings.xml
@@ -81,7 +81,7 @@
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ကို ပယ်ဖျက်နေပါသည်"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"စာထုတ်စက်မှ အမှား <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>ကိုစာထုတ်စက်ကငြင်းလိုက်သည်"</string>
- <string name="cancel" msgid="4373674107267141885">"ဖျက်သိမ်းရန်"</string>
+ <string name="cancel" msgid="4373674107267141885">"မလုပ်တော့ပါ"</string>
<string name="restart" msgid="2472034227037808749">"အစက ပြန်စရန်"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"စာထုတ်စက်နဲ့ ဆက်သွယ်ထားမှု မရှိပါ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"အကြောင်းအရာ မသိရှိ"</string>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index 36eafa5..c31ece3 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -139,7 +139,7 @@
<string name="choose_profile" msgid="8229363046053568878">"Ընտրել պրոֆիլ"</string>
<string name="category_personal" msgid="1299663247844969448">"Անձնական"</string>
<string name="category_work" msgid="8699184680584175622">"Աշխատանքային"</string>
- <string name="development_settings_title" msgid="215179176067683667">"Ծրագրավորման ընտրանքներ"</string>
+ <string name="development_settings_title" msgid="215179176067683667">"Ծրագրավորողի ընտրանքներ"</string>
<string name="development_settings_enable" msgid="542530994778109538">"Միացնել մշակողի ընտրանքները"</string>
<string name="development_settings_summary" msgid="1815795401632854041">"Կարգավորել ընտրանքները ծրագրի ծրագրավորման համար"</string>
<string name="development_settings_not_available" msgid="4308569041701535607">"Ծրագրավորման ընտրանքներն այլևս հասանելի չեն այս օգտվողի համար"</string>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 9bb5694..0acc414 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -71,7 +71,7 @@
<string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"ထည့်သွင်းရန်အသုံးပြုသည်"</string>
<string name="bluetooth_pairing_accept" msgid="6163520056536604875">"အတူတွဲပါ"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"ချိတ်တွဲရန်"</string>
- <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"ဖျက်သိမ်းရန်"</string>
+ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"မလုပ်တော့ပါ"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"ချိတ်တွဲမှုက ချိတ်ဆက်ထားလျှင် သင်၏ အဆက်အသွယ်များ နှင့် ခေါ်ဆိုမှု မှတ်တမ်းကို ရယူခွင့် ပြုသည်။"</string>
<string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့် တွဲချိတ်မရပါ"</string>
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"ပင်နံပါတ် သို့မဟုတ် ဖြတ်သန်းခွင့်ကီးမမှန်ကန်သောကြောင့်<xliff:g id="DEVICE_NAME">%1$s</xliff:g>နှင့် တွဲချိတ်မရပါ။"</string>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index 015478f..7d41acb 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -114,7 +114,7 @@
<string name="tts_engine_security_warning" msgid="8786238102020223650">"Ushbu nutq sintezi mexanizmi barcha yozgan matnlaringizni to‘plab olishi mumkin, jumladan kredit karta raqamlari va parollar kabi shaxsiy ma‘lumotlarni ham. U <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> mexanizmi bilan o‘rnatiladi. Ushbu nutq sintezi mexanizmidan foydalanilsinmi?"</string>
<string name="tts_engine_network_required" msgid="1190837151485314743">"Bu til uchun nutq sintezatorini yoqish uchun Internetga ulaning."</string>
<string name="tts_default_sample_string" msgid="4040835213373086322">"Bu nutq sintezining misoli"</string>
- <string name="tts_status_title" msgid="7268566550242584413">"Birlamchi til"</string>
+ <string name="tts_status_title" msgid="7268566550242584413">"Asosiy til"</string>
<string name="tts_status_ok" msgid="1309762510278029765">"<xliff:g id="LOCALE">%1$s</xliff:g> – to‘liq qo‘llab-quvvatlanadi"</string>
<string name="tts_status_requires_network" msgid="6042500821503226892">"<xliff:g id="LOCALE">%1$s</xliff:g> tili tarmoqqa ulanishi lozim"</string>
<string name="tts_status_not_supported" msgid="4491154212762472495">"<xliff:g id="LOCALE">%1$s</xliff:g> – qo‘llab-quvvatlanmaydi"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 4eca743..6b4c453 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -324,9 +324,11 @@
<string name="enabled_by_admin" msgid="2386503803463071894">"已被管理员启用"</string>
<string name="disabled_by_admin" msgid="3669999613095206948">"已被管理员禁用"</string>
<string name="home" msgid="3256884684164448244">"设置主屏幕"</string>
- <!-- no translation found for battery_labels:0 (8494684293649631252) -->
- <!-- no translation found for battery_labels:1 (8934126114226089439) -->
- <!-- no translation found for battery_labels:2 (1286113608943010849) -->
+ <string-array name="battery_labels">
+ <item msgid="8494684293649631252">"0%"</item>
+ <item msgid="8934126114226089439">"50%"</item>
+ <item msgid="1286113608943010849">"100%"</item>
+ </string-array>
<string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
<string name="remaining_length_format" msgid="7886337596669190587">"还剩 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="screen_zoom_summary_small" msgid="5867245310241621570">"小"</string>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_focus_gain_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_focus_gain_animation.xml
similarity index 100%
rename from packages/SystemUI/res/anim/tv_pip_controls_text_focus_gain_animation.xml
rename to packages/SystemUI/res/anim/tv_pip_controls_focus_gain_animation.xml
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_text_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_focus_loss_animation.xml
similarity index 100%
rename from packages/SystemUI/res/anim/tv_pip_controls_text_focus_lose_animation.xml
rename to packages/SystemUI/res/anim/tv_pip_controls_focus_loss_animation.xml
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml b/packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_loss_animation.xml
similarity index 100%
rename from packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_lose_animation.xml
rename to packages/SystemUI/res/anim/tv_pip_controls_in_recents_focus_loss_animation.xml
diff --git a/packages/SystemUI/res/layout-television/recents_on_tv.xml b/packages/SystemUI/res/layout-television/recents_on_tv.xml
index 82b9f8c..2b78bee 100644
--- a/packages/SystemUI/res/layout-television/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout-television/recents_on_tv.xml
@@ -21,6 +21,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:background="@drawable/recents_tv_background_gradient">
+
<com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
android:id="@+id/task_list"
android:layout_width="wrap_content"
@@ -32,13 +33,13 @@
android:focusable="true"
android:layoutDirection="rtl" />
- <!-- Placeholder view to give focus to the PIP menus. -->
+ <!-- Placeholder view to give focus to the PIP menus in talkback mode -->
<View
android:id="@+id/pip"
android:layout_width="1dp"
android:layout_height="1dp"
android:focusable="true"
- android:visibility="visible" />
+ android:visibility="gone" />
<!-- Placeholder to dismiss during talkback. -->
<ImageView
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 32839d8..63390e2 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -44,7 +44,7 @@
android:layout_height="wrap_content"
/>
- <ScrollView
+ <com.android.systemui.qs.NonInterceptingScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
@@ -52,7 +52,7 @@
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
- </ScrollView>
+ </com.android.systemui.qs.NonInterceptingScrollView>
<include layout="@layout/qs_detail_buttons" />
diff --git a/packages/SystemUI/res/layout/tv_pip_control_button.xml b/packages/SystemUI/res/layout/tv_pip_control_button.xml
index f18a5da..096dda8 100644
--- a/packages/SystemUI/res/layout/tv_pip_control_button.xml
+++ b/packages/SystemUI/res/layout/tv_pip_control_button.xml
@@ -23,15 +23,25 @@
<ImageView android:id="@+id/button"
android:layout_width="34dp"
android:layout_height="34dp"
- android:padding="5dp"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
android:focusable="true"
- android:src="@drawable/ic_fullscreen_white_24dp"
- android:background="@drawable/tv_pip_button_focused"
- android:layerType="software" />
+ android:src="@drawable/tv_pip_button_focused"
+ android:importantForAccessibility="yes" />
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="34dp"
+ android:layout_height="34dp"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:padding="5dp"
+ android:importantForAccessibility="no" />
<TextView android:id="@+id/desc"
android:layout_width="100dp"
android:layout_height="wrap_content"
+ android:layout_below="@id/icon"
+ android:layout_centerHorizontal="true"
android:layout_marginTop="3dp"
android:gravity="center"
android:text="@string/pip_fullscreen"
diff --git a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
index 557bbe6..f157fd5 100644
--- a/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_recents_overlay.xml
@@ -15,7 +15,7 @@
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_horizontal"
android:orientation="vertical">
@@ -33,7 +33,8 @@
android:layout_height="32dp"
android:translationY="-46dp"
android:layout_gravity="top|center_horizontal"
- android:background="@drawable/tv_pip_recents_overlay_scrim" />
+ android:background="@drawable/tv_pip_recents_overlay_scrim"
+ android:alpha="0" />
<com.android.systemui.tv.pip.PipControlsView
android:id="@+id/pip_control_contents"
android:layout_width="wrap_content"
@@ -42,6 +43,8 @@
android:layout_gravity="top|center_horizontal" />
</com.android.systemui.tv.pip.PipRecentsControlsView>
+ <!-- Placeholder view to handle focus change between Recents row and PIP controls
+ in talkback mode -->
<View
android:id="@+id/recents"
android:layout_width="1dp"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
new file mode 100644
index 0000000..15e1c96
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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.systemui.qs;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.ScrollView;
+
+/**
+ * ScrollView that disallows intercepting for touches that can cause scrolling.
+ */
+public class NonInterceptingScrollView extends ScrollView {
+
+ private float mInitialY;
+
+ public NonInterceptingScrollView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mInitialY = ev.getY();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ requestDisallowInterceptTouchEvent(false);
+ break;
+ default:
+ if (canScrollVertically(ev.getY() > mInitialY ? -1 : 1)) {
+ requestDisallowInterceptTouchEvent(true);
+ }
+ break;
+ }
+ return super.onTouchEvent(ev);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
index f05aa3c..feacaa0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java
@@ -154,9 +154,9 @@
event.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
String label = getResources()
- .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+ .getString(!mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
event.setContentDescription(label);
- event.setChecked(mTileState);
+ event.setChecked(!mTileState);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 1a55958..ecb12d3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -81,6 +81,7 @@
private long mLastTabKeyEventTime;
private boolean mIgnoreAltTabRelease;
private boolean mLaunchedFromHome;
+ private boolean mTalkBackEnabled;
private RecentsTvView mRecentsView;
private View mPipView;
@@ -133,15 +134,22 @@
@Override
public void onRecentsFocused() {
- mRecentsView.requestFocus();
- mRecentsView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ if (mTalkBackEnabled) {
+ mTaskStackHorizontalGridView.requestFocus();
+ mTaskStackHorizontalGridView.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+ mTaskStackHorizontalGridView.startFocusGainAnimation();
}
};
+
private final View.OnFocusChangeListener mPipViewFocusChangeListener =
new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
- handlePipViewFocusChange(hasFocus);
+ if (hasFocus) {
+ requestPipControlsFocus();
+ }
}
};
@@ -194,17 +202,18 @@
loadOpts.numVisibleTaskThumbnails = numVisibleTasks;
loader.loadTasks(this, plan, loadOpts);
-
- mRecentsView.setTaskStack(stack);
List stackTasks = stack.getStackTasks();
Collections.reverse(stackTasks);
if (mTaskStackViewAdapter == null) {
mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stackTasks);
mTaskStackHorizontalGridView = mRecentsView
.setTaskStackViewAdapter(mTaskStackViewAdapter);
+ mHomeRecentsEnterExitAnimationHolder = new HomeRecentsEnterExitAnimationHolder(
+ getApplicationContext(), mTaskStackHorizontalGridView);
} else {
mTaskStackViewAdapter.setNewStackTasks(stackTasks);
}
+ mRecentsView.init(stack);
if (launchState.launchedToTaskId != -1) {
ArrayList<Task> tasks = stack.getStackTasks();
@@ -305,6 +314,7 @@
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mPipView = findViewById(R.id.pip);
+ mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
// Place mPipView at the PIP bounds for fine tuned focus handling.
Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
@@ -342,7 +352,6 @@
if(mLaunchedFromHome) {
mHomeRecentsEnterExitAnimationHolder.startEnterAnimation(mPipManager.isPipShown());
}
- mTaskStackViewAdapter.setResetAddedCards(true);
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
@@ -353,19 +362,6 @@
// Update the recent tasks
updateRecentsTasks();
- mHomeRecentsEnterExitAnimationHolder = new HomeRecentsEnterExitAnimationHolder(
- getApplicationContext(), mTaskStackHorizontalGridView);
- if(mTaskStackHorizontalGridView != null &&
- mTaskStackHorizontalGridView.getChildCount() > 0) {
- if(mLaunchedFromHome) {
- mHomeRecentsEnterExitAnimationHolder.setEnterFromHomeStartingAnimationValues();
- } else {
- mHomeRecentsEnterExitAnimationHolder.setEnterFromAppStartingAnimationValues();
- }
- } else {
- mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
- }
-
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state, or if recents was relaunched by AM, without going through
// the normal mechanisms
@@ -387,9 +383,11 @@
} else {
mTaskStackHorizontalGridView.setSelectedPosition(0);
}
+ mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
View dismissPlaceholder = findViewById(R.id.dismiss_placeholder);
- if (ssp.isTouchExplorationEnabled()) {
+ mTalkBackEnabled = ssp.isTouchExplorationEnabled();
+ if (mTalkBackEnabled) {
dismissPlaceholder.setAccessibilityTraversalBefore(R.id.task_list);
dismissPlaceholder.setAccessibilityTraversalAfter(R.id.dismiss_placeholder);
mTaskStackHorizontalGridView.setAccessibilityTraversalAfter(R.id.dismiss_placeholder);
@@ -408,14 +406,29 @@
}
});
}
- updatePipUI();
+
+ // Initialize PIP UI
+ if (mPipManager.isPipShown()) {
+ if (mTalkBackEnabled) {
+ // If talkback is on, use the mPipView to handle focus changes
+ // between recents row and PIP controls.
+ mPipView.setVisibility(View.VISIBLE);
+ } else {
+ mPipView.setVisibility(View.GONE);
+ }
+ // When PIP view has focus, recents overlay view will takes the focus
+ // as if it's the part of the Recents UI.
+ mPipRecentsOverlayManager.requestFocus(mTaskStackViewAdapter.getItemCount() > 0);
+ } else {
+ mPipView.setVisibility(View.GONE);
+ mPipRecentsOverlayManager.removePipRecentsOverlayView();
+ }
}
@Override
public void onPause() {
super.onPause();
mPipRecentsOverlayManager.onRecentsPaused();
- mTaskStackViewAdapter.setResetAddedCards(false);
}
@Override
@@ -534,6 +547,7 @@
public final void onBusEvent(AllTaskViewsDismissedEvent event) {
if (mPipManager.isPipShown()) {
mRecentsView.showEmptyView();
+ mPipRecentsOverlayManager.requestFocus(false);
} else {
dismissRecentsToHome(false);
}
@@ -547,10 +561,14 @@
@Override
public boolean onPreDraw() {
mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
- if(mLaunchedFromHome) {
- mHomeRecentsEnterExitAnimationHolder.setEnterFromHomeStartingAnimationValues();
+ // Sets the initial values for enter animation.
+ // Animation will be started in {@link #onEnterAnimationComplete()}
+ if (mLaunchedFromHome) {
+ mHomeRecentsEnterExitAnimationHolder
+ .setEnterFromHomeStartingAnimationValues(mPipManager.isPipShown());
} else {
- mHomeRecentsEnterExitAnimationHolder.setEnterFromAppStartingAnimationValues();
+ mHomeRecentsEnterExitAnimationHolder
+ .setEnterFromAppStartingAnimationValues(mPipManager.isPipShown());
}
// We post to make sure that this information is delivered after this traversals is
// finished.
@@ -564,35 +582,25 @@
}
private void updatePipUI() {
- if (mPipManager.isPipShown()) {
- mPipView.setVisibility(View.VISIBLE);
- mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
- if (mPipView.hasFocus()) {
- // This can happen only if the activity is resumed. Ask for reset.
- handlePipViewFocusChange(true);
- } else {
- mPipView.requestFocus();
- }
- } else {
- mPipView.setVisibility(View.GONE);
+ if (!mPipManager.isPipShown()) {
mPipRecentsOverlayManager.removePipRecentsOverlayView();
+ mTaskStackHorizontalGridView.startFocusLossAnimation();
+ } else {
+ Log.w(TAG, "An activity entered PIP mode while Recents is shown");
}
}
/**
- * Handles the PIP view's focus change.
+ * Requests the focus to the PIP controls.
* This starts the relevant recents row animation
* and give focus to the recents overlay if needed.
*/
- private void handlePipViewFocusChange(boolean hasFocus) {
- mRecentsView.startRecentsRowFocusAnimation(!hasFocus);
- if (hasFocus) {
- // When PIP view has focus, recents overlay view will takes the focus
- // as if it's the part of the Recents UI.
- mPipRecentsOverlayManager.requestFocus(
- mTaskStackViewAdapter.getItemCount() > 0);
- } else {
- mPipRecentsOverlayManager.clearFocus();
+ public void requestPipControlsFocus() {
+ if (!mPipManager.isPipShown()) {
+ return;
}
+
+ mTaskStackHorizontalGridView.startFocusLossAnimation();
+ mPipRecentsOverlayManager.requestFocus(mTaskStackViewAdapter.getItemCount() > 0);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
index 92718a3..9faaa4b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
@@ -74,19 +74,35 @@
}
}
- public void setEnterFromHomeStartingAnimationValues() {
+ /**
+ * Sets the initial values Recents enter animation
+ * when Recents is started from the Launcher.
+ */
+ public void setEnterFromHomeStartingAnimationValues(boolean isPipShown) {
for(int i = 0; i < mGridView.getChildCount(); i++) {
TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
view.setTranslationX(0);
view.setAlpha(0.0f);
+ view.getInfoFieldView().setAlpha(isPipShown ? 0 : 1f);
+ if (isPipShown && view.hasFocus()) {
+ view.getViewFocusAnimator().changeSize(false);
+ }
}
}
- public void setEnterFromAppStartingAnimationValues() {
+ /**
+ * Sets the initial values Recents enter animation
+ * when Recents is started from an app.
+ */
+ public void setEnterFromAppStartingAnimationValues(boolean isPipShown) {
for(int i = 0; i < mGridView.getChildCount(); i++) {
TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
view.setTranslationX(0);
- view.setAlpha(1.0f);
+ view.setAlpha(isPipShown ? mDimAlpha : 1f);
+ view.getInfoFieldView().setAlpha(isPipShown ? 0 : 1f);
+ if (isPipShown && view.hasFocus()) {
+ view.getViewFocusAnimator().changeSize(false);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
index 45c1537..8a4cf399 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
@@ -29,11 +29,11 @@
* Recents row's focus animation with PIP controls.
*/
public class RecentsRowFocusAnimationHolder {
- private View mView;
- private View mTitleView;
+ private final View mView;
+ private final View mTitleView;
private AnimatorSet mFocusGainAnimatorSet;
- private AnimatorSet mFocusLoseAnimatorSet;
+ private AnimatorSet mFocusLossAnimatorSet;
public RecentsRowFocusAnimationHolder(View view, View titleView) {
mView = view;
@@ -50,28 +50,45 @@
mFocusGainAnimatorSet.setDuration(duration);
mFocusGainAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mFocusLoseAnimatorSet = new AnimatorSet();
- mFocusLoseAnimatorSet.playTogether(
+ mFocusLossAnimatorSet = new AnimatorSet();
+ mFocusLossAnimatorSet.playTogether(
// Animation doesn't start from the current value (1f) sometimes,
// so specify the desired initial value here.
ObjectAnimator.ofFloat(mView, "alpha", 1f, dimAlpha),
ObjectAnimator.ofFloat(mTitleView, "alpha", 0f));
- mFocusLoseAnimatorSet.setDuration(duration);
- mFocusLoseAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mFocusLossAnimatorSet.setDuration(duration);
+ mFocusLossAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
}
/**
- * Returns the Recents row's focus change animation.
+ * Starts the Recents row's focus gain animation.
*/
- public Animator getFocusChangeAnimator(boolean hasFocus) {
- return hasFocus ? mFocusGainAnimatorSet : mFocusLoseAnimatorSet;
+ public void startFocusGainAnimation() {
+ cancelAnimator(mFocusLossAnimatorSet);
+ mFocusGainAnimatorSet.start();
}
/**
- * Resets the views to the initial state immediately.
+ * Starts the Recents row's focus loss animation.
+ */
+ public void startFocusLossAnimation() {
+ cancelAnimator(mFocusGainAnimatorSet);
+ mFocusLossAnimatorSet.start();
+ }
+
+ /**
+ * Resets the views immediately and ends the animations.
*/
public void reset() {
+ cancelAnimator(mFocusLossAnimatorSet);
+ cancelAnimator(mFocusGainAnimatorSet);
mView.setAlpha(1f);
mTitleView.setAlpha(1f);
}
+
+ private static void cancelAnimator(Animator animator) {
+ if (animator.isStarted()) {
+ animator.cancel();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
index 8218599..72fd7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/ViewFocusAnimator.java
@@ -89,7 +89,7 @@
});
}
- public void setFocusProgress(float level) {
+ private void setFocusProgress(float level) {
mFocusProgress = level;
float scale = mUnselectedScale + (level * mSelectedScaleDelta);
@@ -107,33 +107,19 @@
mTargetView.getDismissIconView().setZ(z);
}
- public float getFocusProgress() {
- return mFocusProgress;
- }
-
- public void animateFocus(boolean focused) {
+ private void animateFocus(boolean focused) {
if (mFocusAnimation.isStarted()) {
mFocusAnimation.cancel();
}
float target = focused ? 1.0f : 0.0f;
- if (getFocusProgress() != target) {
- mFocusAnimation.setFloatValues(getFocusProgress(), target);
+ if (mFocusProgress != target) {
+ mFocusAnimation.setFloatValues(mFocusProgress, target);
mFocusAnimation.start();
}
}
- public void setFocusImmediate(boolean focused) {
- if (mFocusAnimation.isStarted()) {
- mFocusAnimation.cancel();
- }
-
- float target = focused ? 1.0f : 0.0f;
-
- setFocusProgress(target);
- }
-
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (v != mTargetView) {
@@ -142,21 +128,27 @@
changeSize(hasFocus);
}
- protected void changeSize(boolean hasFocus) {
+ /**
+ * Changes the size of the {@link TaskCardView} to show its focused state.
+ */
+ public void changeSize(boolean hasFocus) {
ViewGroup.LayoutParams lp = mTargetView.getLayoutParams();
int width = lp.width;
int height = lp.height;
if (width < 0 && height < 0) {
mTargetView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- height = mTargetView.getMeasuredHeight();
}
if (mTargetView.isAttachedToWindow() && mTargetView.hasWindowFocus() &&
mTargetView.getVisibility() == View.VISIBLE) {
animateFocus(hasFocus);
} else {
- setFocusImmediate(hasFocus);
+ // Set focus immediately.
+ if (mFocusAnimation.isStarted()) {
+ mFocusAnimation.cancel();
+ }
+ setFocusProgress(hasFocus ? 1.0f : 0.0f);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index 06b24418..318b7f9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -59,7 +59,7 @@
private boolean mAwaitingFirstLayout = true;
private Rect mSystemInsets = new Rect();
private RecentsTvTransitionHelper mTransitionHelper;
- private Handler mHandler;
+ private final Handler mHandler = new Handler();
private OnScrollListener mScrollListener;
public RecentsTvView(Context context) {
this(context, null);
@@ -81,9 +81,7 @@
LayoutInflater inflater = LayoutInflater.from(context);
mEmptyView = inflater.inflate(R.layout.recents_tv_empty, this, false);
addView(mEmptyView);
- mEmptyViewFocusAnimationHolder = new RecentsRowFocusAnimationHolder(mEmptyView, null);
- mHandler = new Handler();
mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
}
@@ -91,20 +89,18 @@
protected void onFinishInflate() {
super.onFinishInflate();
mDismissPlaceholder = findViewById(R.id.dismiss_placeholder);
+ mTaskStackHorizontalView = (TaskStackHorizontalGridView) findViewById(R.id.task_list);
}
- public void setTaskStack(TaskStack stack) {
+ /**
+ * Initialize the view.
+ */
+ public void init(TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
mStack = stack;
- if (mTaskStackHorizontalView != null) {
- mTaskStackHorizontalView.reset();
- mTaskStackHorizontalView.setStack(stack);
- } else {
- mTaskStackHorizontalView = (TaskStackHorizontalGridView) findViewById(R.id.task_list);
- mTaskStackHorizontalView.setStack(stack);
- }
+ mTaskStackHorizontalView.init(stack);
if (stack.getStackTaskCount() > 0) {
hideEmptyView();
@@ -112,6 +108,7 @@
showEmptyView();
}
+ // Layout with the new stack
requestLayout();
}
@@ -189,17 +186,6 @@
}
/**
- * Starts the focus change animation.
- */
- public void startRecentsRowFocusAnimation(boolean hasFocus) {
- if (mEmptyView.getVisibility() == View.VISIBLE) {
- mEmptyViewFocusAnimationHolder.getFocusChangeAnimator(hasFocus).start();
- } else {
- mTaskStackHorizontalView.startRecentsRowFocusAnimation(hasFocus);
- }
- }
-
- /**
* Hides the task stack and shows the empty view.
*/
public void showEmptyView() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 758f93a..72a589f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -40,16 +40,18 @@
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.tv.RecentsTvActivity;
import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
-import com.android.systemui.recents.model.Task;
public class TaskCardView extends LinearLayout {
private static final String TAG = "TaskCardView";
private View mThumbnailView;
private View mDismissIconView;
+ private View mInfoFieldView;
private TextView mTitleTextView;
private ImageView mBadgeView;
private Task mTask;
@@ -79,14 +81,14 @@
protected void onFinishInflate() {
super.onFinishInflate();
mThumbnailView = findViewById(R.id.card_view_thumbnail);
+ mInfoFieldView = findViewById(R.id.card_info_field);
mTitleTextView = (TextView) findViewById(R.id.card_title_text);
mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
mDismissIconView = findViewById(R.id.dismiss_icon);
mDismissAnimationsHolder = new DismissAnimationsHolder(this);
- View title = findViewById(R.id.card_info_field);
mCornerRadius = getResources().getDimensionPixelSize(
R.dimen.recents_task_view_rounded_corners_radius);
- mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
+ mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, mInfoFieldView);
SystemServicesProxy ssp = Recents.getSystemServices();
if (!ssp.isTouchExplorationEnabled()) {
mDismissIconView.setVisibility(VISIBLE);
@@ -102,6 +104,9 @@
mBadgeView.setImageDrawable(task.icon);
setThumbnailView();
setContentDescription(task.titleDescription);
+ mDismissState = false;
+ mDismissAnimationsHolder.reset();
+ mRecentsRowFocusAnimationHolder.reset();
}
public Task getTask() {
@@ -196,40 +201,37 @@
}
@Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ // Override dispatchKeyEvent() instead of onKeyDown() to prevent warning from ViewRootImpl.
+ switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_DOWN : {
- if (!isInDismissState()) {
+ if (!isInDismissState() && event.getAction() == KeyEvent.ACTION_DOWN) {
setDismissState(true);
return true;
}
break;
}
case KeyEvent.KEYCODE_DPAD_UP : {
- if (isInDismissState()) {
- setDismissState(false);
- return true;
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ if (isInDismissState()) {
+ setDismissState(false);
+ } else {
+ ((RecentsTvActivity) getContext()).requestPipControlsFocus();
+ }
}
- break;
+ return true;
}
- //Eat right and left key presses when we are in dismiss state
- case KeyEvent.KEYCODE_DPAD_LEFT : {
- if (isInDismissState()) {
- return true;
- }
- break;
- }
+ // Eat right and left key presses when we are in dismiss state
+ case KeyEvent.KEYCODE_DPAD_LEFT :
case KeyEvent.KEYCODE_DPAD_RIGHT : {
if (isInDismissState()) {
return true;
}
break;
}
- default:
- break;
}
- return super.onKeyDown(keyCode, event);
+ return super.dispatchKeyEvent(event);
}
private void setDismissState(boolean dismissState) {
@@ -252,22 +254,14 @@
mDismissAnimationsHolder.startDismissAnimation(listener);
}
+ public ViewFocusAnimator getViewFocusAnimator() {
+ return mViewFocusAnimator;
+ }
+
public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() {
return mRecentsRowFocusAnimationHolder;
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- setDismissState(false);
- }
-
- public void reset() {
- mDismissState = false;
- mRecentsRowFocusAnimationHolder.reset();
- mDismissAnimationsHolder.reset();
- }
-
private void setThumbnailView() {
ImageView screenshotView = (ImageView) findViewById(R.id.card_view_banner_icon);
PackageManager pm = getContext().getPackageManager();
@@ -332,6 +326,10 @@
return mThumbnailView;
}
+ public View getInfoFieldView() {
+ return mInfoFieldView;
+ }
+
public View getDismissIconView() {
return mDismissIconView;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 3fb339e..f9b8700 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -18,8 +18,6 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.util.AttributeSet;
import android.view.View;
@@ -39,14 +37,6 @@
public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks {
private static final int ANIMATION_DELAY_MS = 50;
private static final int MSG_START_RECENT_ROW_FOCUS_ANIMATION = 100;
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_START_RECENT_ROW_FOCUS_ANIMATION) {
- startRecentsRowFocusAnimation(msg.arg1 == 1);
- }
- }
- };
private TaskStack mStack;
private Task mFocusedTask;
private AnimatorSet mRecentsRowFocusAnimation;
@@ -74,38 +64,15 @@
}
/**
- * Resets this view for reuse.
- */
- public void reset() {
- for (int i = 0; i < getChildCount(); i++) {
- ((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder().reset();
- }
- if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
- mRecentsRowFocusAnimation.cancel();
- }
- mHandler.removeCallbacksAndMessages(null);
- requestLayout();
- }
-
- /**
- * @param task - Task to reset
- */
- private void resetFocusedTask(Task task) {
- mFocusedTask = null;
- }
-
- /**
- * Sets the task stack.
+ * Initializes the grid view.
* @param stack
*/
- public void setStack(TaskStack stack) {
- //Set new stack
+ public void init(TaskStack stack) {
+ // Set new stack
mStack = stack;
if (mStack != null) {
mStack.setCallbacks(this);
}
- //Layout with new stack
- requestLayout();
}
/**
@@ -126,13 +93,6 @@
}
/**
- * @return - The focused task card view.
- */
- public TaskCardView getFocusedTaskCardView() {
- return ((TaskCardView)findFocus());
- }
-
- /**
* @param task
* @return Child view for given task
*/
@@ -146,32 +106,31 @@
return null;
}
+
/**
- * Starts the focus change animation.
+ * Starts the Recents row's focus gain animation.
*/
- public void startRecentsRowFocusAnimation(final boolean hasFocus) {
- if (getChildCount() == 0) {
- // Animation request may happen before view is attached.
- // Post again with small dealy so animation can be run again later.
- if (getAdapter().getItemCount() > 0) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_START_RECENT_ROW_FOCUS_ANIMATION, hasFocus ? 1 : 0),
- ANIMATION_DELAY_MS);
+ public void startFocusGainAnimation() {
+ for (int i = 0; i < getChildCount(); i++) {
+ TaskCardView v = (TaskCardView) getChildAt(i);
+ if (v.hasFocus()) {
+ v.getViewFocusAnimator().changeSize(true);
}
- return;
+ v.getRecentsRowFocusAnimationHolder().startFocusGainAnimation();
}
- if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
- mRecentsRowFocusAnimation.cancel();
+ }
+
+ /**
+ * Starts the Recents row's focus loss animation.
+ */
+ public void startFocusLossAnimation() {
+ for (int i = 0; i < getChildCount(); i++) {
+ TaskCardView v = (TaskCardView) getChildAt(i);
+ if (v.hasFocus()) {
+ v.getViewFocusAnimator().changeSize(false);
+ }
+ v.getRecentsRowFocusAnimationHolder().startFocusLossAnimation();
}
- Animator animator = ((TaskCardView) getChildAt(0)).getRecentsRowFocusAnimationHolder()
- .getFocusChangeAnimator(hasFocus);
- mRecentsRowFocusAnimation = new AnimatorSet();
- AnimatorSet.Builder builder = mRecentsRowFocusAnimation.play(animator);
- for (int i = 1; i < getChildCount(); i++) {
- builder.with(((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder()
- .getFocusChangeAnimator(hasFocus));
- }
- mRecentsRowFocusAnimation.start();
}
@Override
@@ -185,7 +144,7 @@
AnimationProps animation, boolean fromDockGesture) {
((TaskStackHorizontalViewAdapter) getAdapter()).removeTask(removedTask);
if (mFocusedTask == removedTask) {
- resetFocusedTask(removedTask);
+ mFocusedTask = null;
}
// If there are no remaining tasks, then just close recents
if (mStack.getStackTaskCount() == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index b6b86b4..236d077 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -43,17 +43,13 @@
private static final String TAG = "TaskStackViewAdapter";
private List<Task> mTaskList;
private TaskStackHorizontalGridView mGridView;
- private boolean mResetAddedCards;
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TaskCardView mTaskCardView;
private Task mTask;
- private boolean mShouldReset;
public ViewHolder(View v) {
super(v);
- if(v instanceof TaskCardView) {
- mTaskCardView = (TaskCardView) v;
- }
+ mTaskCardView = (TaskCardView) v;
}
public void init(Task task) {
@@ -90,7 +86,6 @@
public void onAnimationEnd(Animator animation) {
removeTask(task);
EventBus.getDefault().send(new DeleteTaskDataEvent(task));
- mShouldReset = true;
}
@Override
@@ -131,23 +126,6 @@
}
@Override
- public void onViewAttachedToWindow(ViewHolder holder) {
- if (mResetAddedCards) {
- holder.mTaskCardView.reset();
- }
- }
-
- @Override
- public void onViewDetachedFromWindow(ViewHolder holder) {
- // We only want to reset on view detach if this is the last task being dismissed.
- // This is so that we do not reset when shifting to apps etc, as it is not needed.
- if (holder.mShouldReset) {
- holder.mTaskCardView.reset();
- holder.mShouldReset = false;
- }
- }
-
- @Override
public int getItemCount() {
return mTaskList.size();
}
@@ -178,8 +156,4 @@
mTaskList.add(position, task);
notifyItemInserted(position);
}
-
- public void setResetAddedCards(boolean reset) {
- mResetAddedCards = reset;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 77b7338..e3fe1ab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -404,7 +404,7 @@
* Sets the system insets.
*/
public boolean setSystemInsets(Rect systemInsets) {
- boolean changed = mSystemInsets.equals(systemInsets);
+ boolean changed = !mSystemInsets.equals(systemInsets);
mSystemInsets.set(systemInsets);
return changed;
}
@@ -583,11 +583,25 @@
if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) {
if (ignoreScrollToFront || (!launchState.launchedWithAltTab && !scrollToFront)) {
// Set the initial scroll to the predefined state (which differs from the stack)
- float [] initialNormX = new float[] {
- getNormalizedXFromUnfocusedY(mSystemInsets.bottom + mInitialBottomOffset,
- FROM_BOTTOM),
- getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP)
- };
+ float [] initialNormX = null;
+ float minBottomTaskNormX = getNormalizedXFromUnfocusedY(mSystemInsets.bottom +
+ mInitialBottomOffset, FROM_BOTTOM);
+ float maxBottomTaskNormX = getNormalizedXFromUnfocusedY(mFocusedTopPeekHeight +
+ mTaskRect.height() - mMinMargin, FROM_TOP);
+ if (mNumStackTasks <= 2) {
+ // For small stacks, position the tasks so that they are top aligned to under
+ // the action button, but ensure that it is at least a certain offset from the
+ // bottom of the stack
+ initialNormX = new float[] {
+ Math.min(maxBottomTaskNormX, minBottomTaskNormX),
+ getNormalizedXFromUnfocusedY(mFocusedTopPeekHeight, FROM_TOP)
+ };
+ } else {
+ initialNormX = new float[] {
+ minBottomTaskNormX,
+ getNormalizedXFromUnfocusedY(mInitialTopOffset, FROM_TOP)
+ };
+ }
mUnfocusedRange.offset(0f);
List<Task> tasks = stack.getStackTasks();
@@ -881,14 +895,7 @@
TaskViewTransform frontTransform, boolean ignoreSingleTaskCase, boolean forceUpdate) {
SystemServicesProxy ssp = Recents.getSystemServices();
- // Compute the focused and unfocused offset
- float boundedStackScroll = Utilities.clamp(stackScroll, mMinScrollP, mMaxScrollP);
- mUnfocusedRange.offset(boundedStackScroll);
- mFocusedRange.offset(boundedStackScroll);
- float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
- float boundedScrollFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
- float boundedScrollUnfocusedNonOverrideRangeX =
- mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress);
+ // Ensure that the task is in range
mUnfocusedRange.offset(stackScroll);
mFocusedRange.offset(stackScroll);
boolean unfocusedVisible = mUnfocusedRange.isInRange(taskProgress);
@@ -900,9 +907,30 @@
return;
}
+ // Map the absolute task progress to the normalized x at the stack scroll. We use this to
+ // calculate positions along the curve.
+ mUnfocusedRange.offset(stackScroll);
+ mFocusedRange.offset(stackScroll);
float unfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
float focusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
+ // Map the absolute task progress to the normalized x at the bounded stack scroll. We use
+ // this to calculate bounded properties, like translationZ and outline alpha.
+ float boundedStackScroll = Utilities.clamp(stackScroll, mMinScrollP, mMaxScrollP);
+ mUnfocusedRange.offset(boundedStackScroll);
+ mFocusedRange.offset(boundedStackScroll);
+ float boundedScrollUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
+ float boundedScrollUnfocusedNonOverrideRangeX =
+ mUnfocusedRange.getNormalizedX(nonOverrideTaskProgress);
+
+ // Map the absolute task progress to the normalized x at the upper bounded stack scroll.
+ // We use this to calculate the dim, which is bounded only on one end.
+ float lowerBoundedStackScroll = Utilities.clamp(stackScroll, -Float.MAX_VALUE, mMaxScrollP);
+ mUnfocusedRange.offset(lowerBoundedStackScroll);
+ mFocusedRange.offset(lowerBoundedStackScroll);
+ float lowerBoundedUnfocusedRangeX = mUnfocusedRange.getNormalizedX(taskProgress);
+ float lowerBoundedFocusedRangeX = mFocusedRange.getNormalizedX(taskProgress);
+
int x = (mStackRect.width() - mTaskRect.width()) / 2;
int y;
float z;
@@ -917,7 +945,8 @@
y = centerYOffset + getYForDeltaP(tmpP, 0);
z = mMaxTranslationZ;
dimAlpha = 0f;
- viewOutlineAlpha = (OUTLINE_ALPHA_MIN_VALUE + OUTLINE_ALPHA_MAX_VALUE) / 2f;
+ viewOutlineAlpha = OUTLINE_ALPHA_MIN_VALUE +
+ (OUTLINE_ALPHA_MAX_VALUE - OUTLINE_ALPHA_MIN_VALUE) / 2f;
} else {
// Otherwise, update the task to the stack layout
@@ -926,9 +955,22 @@
int focusedY = (int) ((1f - mFocusedCurveInterpolator.getInterpolation(
focusedRangeX)) * mStackRect.height());
float unfocusedDim = mUnfocusedDimCurveInterpolator.getInterpolation(
- boundedScrollUnfocusedRangeX);
+ lowerBoundedUnfocusedRangeX);
float focusedDim = mFocusedDimCurveInterpolator.getInterpolation(
- boundedScrollFocusedRangeX);
+ lowerBoundedFocusedRangeX);
+
+ // Special case, because we override the initial task positions differently for small
+ // stacks, we clamp the dim to 0 in the initial position, and then only modulate the
+ // dim when the task is scrolled back towards the top of the screen
+ if (mNumStackTasks <= 2 && nonOverrideTaskProgress == 0f) {
+ if (boundedScrollUnfocusedRangeX >= 0.5f) {
+ unfocusedDim = 0f;
+ } else {
+ float offset = mUnfocusedDimCurveInterpolator.getInterpolation(0.5f);
+ unfocusedDim -= offset;
+ unfocusedDim *= MAX_DIM / (MAX_DIM - offset);
+ }
+ }
y = (mStackRect.top - mTaskRect.top) +
(int) Utilities.mapRange(focusState, unfocusedY, focusedY);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 0d9f77c..231360e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1996,6 +1996,10 @@
if (event.fromDeviceOrientationChange) {
mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
mDisplayRect = Recents.getSystemServices().getDisplayRect();
+
+ // Always stop the scroller, otherwise, we may continue setting the stack scroll to the
+ // wrong bounds in the new layout
+ mStackScroller.stopScroller();
}
reloadOnConfigurationChange();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index e89c136..a0bae20 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -39,6 +39,7 @@
public class Divider extends SystemUI {
private DividerWindowManager mWindowManager;
private DividerView mView;
+ private final DividerState mDividerState = new DividerState();
private DockDividerVisibilityListener mDockDividerVisibilityListener;
private boolean mVisible = false;
private boolean mMinimized = false;
@@ -76,7 +77,7 @@
final int width = landscape ? size : MATCH_PARENT;
final int height = landscape ? MATCH_PARENT : size;
mWindowManager.add(mView, width, height);
- mView.setWindowManager(mWindowManager);
+ mView.injectDependencies(mWindowManager, mDividerState);
}
private void removeDivider() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerState.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerState.java
new file mode 100644
index 0000000..353a974
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerState.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 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.systemui.stackdivider;
+
+/**
+ * Class to hold state of divider that needs to persist across configuration changes.
+ */
+public class DividerState {
+ public boolean animateAfterRecentsDrawn;
+ public boolean growAfterRecentsDrawn;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index f728aa4..d09587b 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -30,12 +30,12 @@
import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
+import android.os.Handler;
import android.util.AttributeSet;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.VelocityTracker;
@@ -141,8 +141,6 @@
private DividerSnapAlgorithm mSnapAlgorithm;
private final Rect mStableInsets = new Rect();
- private boolean mAnimateAfterRecentsDrawn;
- private boolean mGrowAfterRecentsDrawn;
private boolean mGrowRecents;
private ValueAnimator mCurrentAnimator;
private boolean mEntranceAnimationRunning;
@@ -151,6 +149,8 @@
private GestureDetector mGestureDetector;
private boolean mDockedStackMinimized;
private boolean mAdjustedForIme;
+ private DividerState mState;
+ private final Handler mHandler = new Handler();
private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() {
@Override
@@ -335,8 +335,9 @@
}
}
- public void setWindowManager(DividerWindowManager windowManager) {
+ public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState) {
mWindowManager = windowManager;
+ mState = dividerState;
}
public WindowManagerProxy getWindowManagerProxy() {
@@ -558,7 +559,7 @@
if (endDelay == 0 || mCancelled) {
endAction.run();
} else {
- postDelayed(endAction, endDelay);
+ mHandler.postDelayed(endAction, endDelay);
}
}
});
@@ -1048,15 +1049,15 @@
public final void onBusEvent(RecentsActivityStartingEvent recentsActivityStartingEvent) {
if (mGrowRecents && getWindowManagerProxy().getDockSide() == WindowManager.DOCKED_TOP
&& getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position) {
- mGrowAfterRecentsDrawn = true;
+ mState.growAfterRecentsDrawn = true;
startDragging(false /* animate */, false /* touching */);
}
}
public final void onBusEvent(DockedTopTaskEvent event) {
if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) {
- mGrowAfterRecentsDrawn = false;
- mAnimateAfterRecentsDrawn = true;
+ mState.growAfterRecentsDrawn = false;
+ mState.animateAfterRecentsDrawn = true;
startDragging(false /* animate */, false /* touching */);
}
updateDockSide();
@@ -1068,11 +1069,11 @@
}
public final void onBusEvent(RecentsDrawnEvent drawnEvent) {
- if (mAnimateAfterRecentsDrawn) {
- mAnimateAfterRecentsDrawn = false;
+ if (mState.animateAfterRecentsDrawn) {
+ mState.animateAfterRecentsDrawn = false;
updateDockSide();
- post(() -> {
+ mHandler.post(() -> {
// Delay switching resizing mode because this might cause jank in recents animation
// that's longer than this animation.
stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(),
@@ -1080,8 +1081,8 @@
200 /* endDelay */);
});
}
- if (mGrowAfterRecentsDrawn) {
- mGrowAfterRecentsDrawn = false;
+ if (mState.growAfterRecentsDrawn) {
+ mState.growAfterRecentsDrawn = false;
updateDockSide();
EventBus.getDefault().send(new RecentsGrowingEvent());
stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 336,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 63933d4..91889d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1887,10 +1887,13 @@
public boolean onDismiss() {
if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
// Release the HUN notification to the shade.
+
+ if (isPanelFullyCollapsed()) {
+ HeadsUpManager.setIsClickedNotification(row, true);
+ }
//
// In most cases, when FLAG_AUTO_CANCEL is set, the notification will
// become canceled shortly by NoMan, but we can't assume that.
- HeadsUpManager.setIsClickedNotification(row, true);
mHeadsUpManager.releaseImmediately(notificationKey);
}
StatusBarNotification parentToCancel = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 6f6b02d..6e9e830 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1486,11 +1486,26 @@
updateBackground();
}
+ public int getPositionOfChild(ExpandableNotificationRow childRow) {
+ if (mIsSummaryWithChildren) {
+ return mChildrenContainer.getPositionInLinearLayout(childRow);
+ }
+ return 0;
+ }
+
public void setExpansionLogger(ExpansionLogger logger, String key) {
mLogger = logger;
mLoggingKey = key;
}
+ public void onExpandedByGesture(boolean userExpanded) {
+ int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER;
+ if (mGroupManager.isSummaryOfGroup(getStatusBarNotification())) {
+ event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER;
+ }
+ MetricsLogger.action(mContext, event, userExpanded);
+ }
+
@Override
public float getIncreasedPaddingAmount() {
if (mIsSummaryWithChildren) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index f3c666f..d6c02e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -360,7 +360,9 @@
private void setVisible(final boolean isVisible) {
if (isVisible) {
-
+ // This call can happen multiple times, but removing only removes a single one.
+ // We therefore need to remove the old one.
+ getViewTreeObserver().removeOnPreDrawListener(mEnableAnimationPredrawListener);
// We only animate if we are drawn at least once, otherwise the view might animate when
// it's shown the first time
getViewTreeObserver().addOnPreDrawListener(mEnableAnimationPredrawListener);
@@ -401,12 +403,17 @@
return mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.notification_action_list_height);
}
+ int hint;
if (mHeadsUpChild != null) {
- return mHeadsUpChild.getHeight();
+ hint = mHeadsUpChild.getHeight();
} else {
- return mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_action_list_height);
+ hint = mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_action_list_height);
}
+ if (mExpandedChild != null) {
+ hint = Math.min(hint, mExpandedChild.getHeight());
+ }
+ return hint;
}
private void updateContentTransformation() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
index b66e9f3..8463e06 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeaderTransformState.java
@@ -109,9 +109,6 @@
@Override
public void recycle() {
super.recycle();
- if (mWorkProfileState != null) {
- mWorkProfileState.recycle();
- }
sInstancePool.release(this);
}
@@ -120,6 +117,10 @@
super.reset();
mExpandButton = null;
mWorkProfileState = null;
+ if (mWorkProfileState != null) {
+ mWorkProfileState.recycle();
+ mWorkProfileState = null;
+ }
}
public void setVisible(boolean visible) {
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 6877f98..268c46b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3018,7 +3018,7 @@
if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed
&& event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
&& event.getX() == 0 && event.getY() == 0 // a touch outside both bars
- ) {
+ && !mRemoteInputController.isRemoteInputActive()) { // not due to typing in IME
userAutohide();
}
}
@@ -4337,6 +4337,8 @@
// ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
+
+ /* Only ever called as a consequence of a lockscreen expansion gesture. */
@Override
public boolean onDraggedDown(View startingChild, int dragLengthY) {
if (hasActiveNotifications()) {
@@ -4347,6 +4349,10 @@
// We have notifications, go to locked shade.
goToLockedShade(startingChild);
+ if (startingChild instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
+ row.onExpandedByGesture(true /* drag down is always an open */);
+ }
return true;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 2f522f0..40ff051 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -47,6 +47,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.MetricsProto;
import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.stack.ScrollContainer;
@@ -284,11 +285,17 @@
private void findScrollContainer() {
if (mScrollContainer == null) {
+ mScrollContainerChild = null;
ViewParent p = this;
while (p != null) {
+ if (mScrollContainerChild == null && p instanceof ExpandableView) {
+ mScrollContainerChild = (View) p;
+ }
if (p.getParent() instanceof ScrollContainer) {
mScrollContainer = (ScrollContainer) p.getParent();
- mScrollContainerChild = (View) p;
+ if (mScrollContainerChild == null) {
+ mScrollContainerChild = (View) p;
+ }
break;
}
p = p.getParent();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index cb0b848..a0f1bc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -677,6 +677,10 @@
mHeaderUtil = new NotificationHeaderUtil(mNotificationParent);
}
+ public ExpandableNotificationRow getNotificationParent() {
+ return mNotificationParent;
+ }
+
public NotificationHeaderView getHeaderView() {
return mNotificationHeader;
}
@@ -854,4 +858,23 @@
child.setRemoved();
}
}
+
+ public int getPositionInLinearLayout(View childInGroup) {
+ int position = mNotificationHeaderMargin + mNotificatonTopPadding;
+
+ for (int i = 0; i < mChildren.size(); i++) {
+ ExpandableNotificationRow child = mChildren.get(i);
+ boolean notGone = child.getVisibility() != View.GONE;
+ if (notGone) {
+ position += mDividerHeight;
+ }
+ if (child == childInGroup) {
+ return position;
+ }
+ if (notGone) {
+ position += child.getIntrinsicHeight();
+ }
+ }
+ return 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 19f4074..b58538d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -610,9 +610,13 @@
ExpandableView expandableView = (ExpandableView) mForcedScroll;
int positionInLinearLayout = getPositionInLinearLayout(expandableView);
int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
+ int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
- if (mOwnScrollY < targetScroll || positionInLinearLayout < mOwnScrollY) {
+
+ // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
+ // that it is not visible anymore.
+ if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
mOwnScrollY = targetScroll;
}
}
@@ -959,11 +963,13 @@
&& (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
}
+ /* Only ever called as a consequence of an expansion gesture in the shade. */
@Override
public void setUserExpandedChild(View v, boolean userExpanded) {
if (v instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) v).setUserExpanded(userExpanded,
- true /* allowChildrenExpansion */);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ row.setUserExpanded(userExpanded, true /* allowChildrenExpansion */);
+ row.onExpandedByGesture(userExpanded);
}
}
@@ -1032,9 +1038,13 @@
@Override
public boolean scrollTo(View v) {
ExpandableView expandableView = (ExpandableView) v;
- int targetScroll = targetScrollForView(expandableView, getPositionInLinearLayout(v));
+ int positionInLinearLayout = getPositionInLinearLayout(v);
+ int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
+ int outOfViewScroll = positionInLinearLayout + expandableView.getIntrinsicHeight();
- if (mOwnScrollY < targetScroll) {
+ // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
+ // that it is not visible anymore.
+ if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
mDontReportNextOverScroll = true;
postInvalidateOnAnimation();
@@ -1062,6 +1072,10 @@
// animating away. To work around that we'll wait until things have settled.
removeCallbacks(mReclamp);
postDelayed(mReclamp, 50);
+ } else if (mForcedScroll != null) {
+ // The scroll was requested before we got the actual inset - in case we need
+ // to scroll up some more do so now.
+ scrollTo(mForcedScroll);
}
return insets;
}
@@ -2361,7 +2375,15 @@
return view.getHeight();
}
- private int getPositionInLinearLayout(View requestedChild) {
+ private int getPositionInLinearLayout(View requestedView) {
+ ExpandableNotificationRow childInGroup = null;
+ ExpandableNotificationRow requestedRow = null;
+ if (isChildInGroup(requestedView)) {
+ // We're asking for a child in a group. Calculate the position of the parent first,
+ // then within the parent.
+ childInGroup = (ExpandableNotificationRow) requestedView;
+ requestedView = requestedRow = childInGroup.getNotificationParent();
+ }
int position = 0;
float previousIncreasedAmount = 0.0f;
for (int i = 0; i < getChildCount(); i++) {
@@ -2377,7 +2399,10 @@
}
previousIncreasedAmount = increasedPaddingAmount;
}
- if (child == requestedChild) {
+ if (child == requestedView) {
+ if (requestedRow != null) {
+ position += requestedRow.getPositionOfChild(childInGroup);
+ }
return position;
}
if (notGone) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index d6c5506..8f0cd8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -26,9 +26,8 @@
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
+import java.util.WeakHashMap;
/**
* A state of a {@link com.android.systemui.statusbar.stack.NotificationStackScrollLayout} which
@@ -39,12 +38,12 @@
private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
private final ViewGroup mHostView;
- private Map<ExpandableView, StackViewState> mStateMap;
+ private WeakHashMap<ExpandableView, StackViewState> mStateMap;
private final int mClearAllTopPadding;
public StackScrollState(ViewGroup hostView) {
mHostView = hostView;
- mStateMap = new HashMap<ExpandableView, StackViewState>();
+ mStateMap = new WeakHashMap<>();
mClearAllTopPadding = hostView.getContext().getResources().getDimensionPixelSize(
R.dimen.clear_all_padding_top);
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java
index bcf2f67..80c593c 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlButtonView.java
@@ -21,12 +21,11 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View.OnFocusChangeListener;
import android.view.View;
import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.systemui.R;
@@ -34,31 +33,28 @@
/**
* A view containing PIP controls including fullscreen, close, and media controls.
*/
-public class PipControlButtonView extends LinearLayout {
+public class PipControlButtonView extends RelativeLayout {
private OnFocusChangeListener mFocusChangeListener;
- private ImageView mButtonImageView;
+ private ImageView mIconImageView;
+ ImageView mButtonImageView;
private TextView mDescriptionTextView;
- private Animator mFocusGainAnimator;
- private Animator mFocusLoseAnimator;
+ private Animator mTextFocusGainAnimator;
+ private Animator mButtonFocusGainAnimator;
+ private Animator mTextFocusLossAnimator;
+ private Animator mButtonFocusLossAnimator;
private final OnFocusChangeListener mInternalFocusChangeListener =
new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
- if (mFocusLoseAnimator.isStarted()) {
- mFocusLoseAnimator.cancel();
- }
- mFocusGainAnimator.start();
+ startFocusGainAnimation();
} else {
- if (mFocusGainAnimator.isStarted()) {
- mFocusGainAnimator.cancel();
- }
- mFocusLoseAnimator.start();
+ startFocusLossAnimation();
}
if (mFocusChangeListener != null) {
- mFocusChangeListener.onFocusChange(v, hasFocus);
+ mFocusChangeListener.onFocusChange(PipControlButtonView.this, hasFocus);
}
}
};
@@ -82,9 +78,7 @@
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.tv_pip_control_button, this);
- setOrientation(LinearLayout.VERTICAL);
- setGravity(Gravity.CENTER);
-
+ mIconImageView = (ImageView) findViewById(R.id.icon);
mButtonImageView = (ImageView) findViewById(R.id.button);
mDescriptionTextView = (TextView) findViewById(R.id.desc);
@@ -103,12 +97,19 @@
super.onFinishInflate();
mButtonImageView.setOnFocusChangeListener(mInternalFocusChangeListener);
- mFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_text_focus_gain_animation);
- mFocusGainAnimator.setTarget(mDescriptionTextView);
- mFocusLoseAnimator = AnimatorInflater.loadAnimator(getContext(),
- R.anim.tv_pip_controls_text_focus_lose_animation);
- mFocusLoseAnimator.setTarget(mDescriptionTextView);
+ mTextFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
+ R.anim.tv_pip_controls_focus_gain_animation);
+ mTextFocusGainAnimator.setTarget(mDescriptionTextView);
+ mButtonFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
+ R.anim.tv_pip_controls_focus_gain_animation);
+ mButtonFocusGainAnimator.setTarget(mButtonImageView);
+
+ mTextFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(),
+ R.anim.tv_pip_controls_focus_loss_animation);
+ mTextFocusLossAnimator.setTarget(mDescriptionTextView);
+ mButtonFocusLossAnimator = AnimatorInflater.loadAnimator(getContext(),
+ R.anim.tv_pip_controls_focus_loss_animation);
+ mButtonFocusLossAnimator.setTarget(mButtonImageView);
}
@Override
@@ -125,7 +126,7 @@
* Sets the drawable for the button with the given resource id.
*/
public void setImageResource(int resId) {
- mButtonImageView.setImageResource(resId);
+ mIconImageView.setImageResource(resId);
}
/**
@@ -136,8 +137,51 @@
mDescriptionTextView.setText(resId);
}
- @Override
- public boolean isFocused() {
- return mButtonImageView.isFocused();
+ private static void cancelAnimator(Animator animator) {
+ if (animator.isStarted()) {
+ animator.cancel();
+ }
+ }
+
+ /**
+ * Starts the focus gain animation.
+ */
+ public void startFocusGainAnimation() {
+ cancelAnimator(mButtonFocusLossAnimator);
+ cancelAnimator(mTextFocusLossAnimator);
+ mTextFocusGainAnimator.start();
+ if (mButtonImageView.getAlpha() < 1f) {
+ // If we had faded out the ripple drawable, run our manual focus change animation.
+ // See the comment at {@link #startFocusLossAnimation()} for the reason of manual
+ // animator.
+ mButtonFocusGainAnimator.start();
+ }
+ }
+
+ /**
+ * Starts the focus loss animation.
+ */
+ public void startFocusLossAnimation() {
+ cancelAnimator(mButtonFocusGainAnimator);
+ cancelAnimator(mTextFocusGainAnimator);
+ mTextFocusLossAnimator.start();
+ if (mButtonImageView.hasFocus()) {
+ // Button uses ripple that has the default animation for the focus changes.
+ // Howevever, it doesn't expose the API to fade out while it is focused,
+ // so we should manually run the fade out animation when PIP controls row loses focus.
+ mButtonFocusLossAnimator.start();
+ }
+ }
+
+ /**
+ * Resets to initial state.
+ */
+ public void reset() {
+ cancelAnimator(mButtonFocusGainAnimator);
+ cancelAnimator(mTextFocusGainAnimator);
+ cancelAnimator(mButtonFocusLossAnimator);
+ cancelAnimator(mTextFocusLossAnimator);
+ mButtonImageView.setAlpha(1f);
+ mDescriptionTextView.setAlpha(mButtonImageView.hasFocus() ? 1f : 0f);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
index 5614bf9..71740ce 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
@@ -61,8 +61,7 @@
private PipControlButtonView mCloseButtonView;
private PipControlButtonView mPlayPauseButtonView;
- private boolean mHasFocus;
- private OnFocusChangeListener mOnChildFocusChangeListener;
+ private PipControlButtonView mFocusedChild;
private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
@Override
@@ -80,8 +79,12 @@
private final OnFocusChangeListener mFocusChangeListener = new OnFocusChangeListener() {
@Override
- public void onFocusChange(View v, boolean hasFocus) {
- onChildViewFocusChanged();
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (hasFocus) {
+ mFocusedChild = (PipControlButtonView) view;
+ } else if (mFocusedChild == view) {
+ mFocusedChild = null;
+ }
}
};
@@ -200,23 +203,13 @@
}
/**
- * Sets a listener to be invoked when {@link android.view.View.hasFocus()} is changed.
+ * Resets to initial state.
*/
- public void setOnChildFocusChangeListener(OnFocusChangeListener listener) {
- mOnChildFocusChangeListener = listener;
- }
-
- private void onChildViewFocusChanged() {
- // At this moment, hasFocus() returns true although there's no focused child.
- boolean hasFocus = (mFullButtonView != null && mFullButtonView.isFocused())
- || (mPlayPauseButtonView != null && mPlayPauseButtonView.isFocused())
- || (mCloseButtonView != null && mCloseButtonView.isFocused());
- if (mHasFocus != hasFocus) {
- mHasFocus = hasFocus;
- if (mOnChildFocusChangeListener != null) {
- mOnChildFocusChangeListener.onFocusChange(getFocusedChild(), mHasFocus);
- }
- }
+ public void reset() {
+ mFullButtonView.reset();
+ mCloseButtonView.reset();
+ mPlayPauseButtonView.reset();
+ mFullButtonView.requestFocus();
}
/**
@@ -225,4 +218,11 @@
public void setListener(Listener listener) {
mListener = listener;
}
+
+ /**
+ * Returns the focused control button view to animate focused button.
+ */
+ PipControlButtonView getFocusedButton() {
+ return mFocusedChild;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index 476598d..30622d2 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -126,7 +126,6 @@
private int mSuspendPipResizingReason;
private Context mContext;
- private SystemServicesProxy mSystemServiceProxy;
private PipRecentsOverlayManager mPipRecentsOverlayManager;
private IActivityManager mActivityManager;
private MediaSessionManager mMediaSessionManager;
@@ -213,8 +212,7 @@
mPipBounds = mDefaultPipBounds;
mActivityManager = ActivityManagerNative.getDefault();
- mSystemServiceProxy = SystemServicesProxy.getInstance(context);
- mSystemServiceProxy.registerTaskStackListener(mTaskStackListener);
+ SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
index df44dc1..ffe96af 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsControlsView.java
@@ -48,12 +48,12 @@
abstract void onBackPressed();
}
- final PipManager mPipManager = PipManager.getInstance();
+ private final PipManager mPipManager = PipManager.getInstance();
private Listener mListener;
private PipControlsView mPipControlsView;
private View mScrim;
private Animator mFocusGainAnimator;
- private AnimatorSet mFocusLoseAnimatorSet;
+ private AnimatorSet mFocusLossAnimatorSet;
public PipRecentsControlsView(Context context) {
this(context, null, 0, 0);
@@ -80,12 +80,12 @@
mScrim = findViewById(R.id.scrim);
mFocusGainAnimator = loadAnimator(mPipControlsView,
- R.anim.tv_pip_controls_in_recents_focus_gain_animation);
+ R.anim.tv_pip_controls_in_recents_focus_gain_animation);
- mFocusLoseAnimatorSet = new AnimatorSet();
- mFocusLoseAnimatorSet.playSequentially(
+ mFocusLossAnimatorSet = new AnimatorSet();
+ mFocusLossAnimatorSet.playSequentially(
loadAnimator(mPipControlsView,
- R.anim.tv_pip_controls_in_recents_focus_lose_animation),
+ R.anim.tv_pip_controls_in_recents_focus_loss_animation),
loadAnimator(mScrim, R.anim.tv_pip_controls_in_recents_scrim_fade_in_animation));
Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
@@ -99,21 +99,29 @@
}
/**
- * Starts focus gaining animation.
+ * Starts focus gain animation.
*/
public void startFocusGainAnimation() {
// Hides the scrim view as soon as possible, before the PIP resize animation starts.
// If we don't, PIP will be moved down a bit and a gap between the scrim and PIP will be
// shown at the bottom of the PIP.
mScrim.setAlpha(0);
- startAnimator(mFocusGainAnimator, mFocusLoseAnimatorSet);
+ PipControlButtonView focus = mPipControlsView.getFocusedButton();
+ if (focus != null) {
+ focus.startFocusGainAnimation();
+ }
+ startAnimator(mFocusGainAnimator, mFocusLossAnimatorSet);
}
/**
- * Starts focus losing animation.
+ * Starts focus loss animation.
*/
- public void startFocusLoseAnimation() {
- startAnimator(mFocusLoseAnimatorSet, mFocusGainAnimator);
+ public void startFocusLossAnimation() {
+ PipControlButtonView focus = mPipControlsView.getFocusedButton();
+ if (focus != null) {
+ focus.startFocusLossAnimation();
+ }
+ startAnimator(mFocusLossAnimatorSet, mFocusGainAnimator);
}
/**
@@ -121,14 +129,14 @@
*/
public void reset() {
cancelAnimator(mFocusGainAnimator);
- cancelAnimator(mFocusLoseAnimatorSet);
+ cancelAnimator(mFocusLossAnimatorSet);
// Reset to initial state (i.e. end of focused)
- requestFocus();
+ mScrim.setAlpha(0);
mPipControlsView.setTranslationY(0);
mPipControlsView.setScaleX(1);
mPipControlsView.setScaleY(1);
- mScrim.setAlpha(0);
+ mPipControlsView.reset();
}
private static void startAnimator(Animator animator, Animator previousAnimator) {
@@ -153,13 +161,20 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (!event.isCanceled()
- && event.getKeyCode() == KeyEvent.KEYCODE_BACK
- && event.getAction() == KeyEvent.ACTION_UP) {
- if (mPipControlsView.mListener != null) {
- ((PipRecentsControlsView.Listener) mPipControlsView.mListener).onBackPressed();
+ if (!event.isCanceled()) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
+ && event.getAction() == KeyEvent.ACTION_UP) {
+ if (mPipControlsView.mListener != null) {
+ ((PipRecentsControlsView.Listener) mPipControlsView.mListener).onBackPressed();
+ }
+ return true;
+ } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_DOWN) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ mPipManager.getPipRecentsOverlayManager().clearFocus();
+ }
+ // Consume the down event always to prevent warning logs from ViewRootImpl.
+ return true;
}
- return true;
}
return super.dispatchKeyEvent(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
index 6e4a593..895b8a2 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -18,15 +18,20 @@
import android.content.Context;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.WindowManager.LayoutParams;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.R;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import static android.view.Gravity.CENTER_HORIZONTAL;
+import static android.view.Gravity.TOP;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
import static com.android.systemui.tv.pip.PipManager.STATE_PIP_OVERLAY;
import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS;
import static com.android.systemui.tv.pip.PipManager.STATE_PIP_RECENTS_FOCUSED;
@@ -42,13 +47,16 @@
private final PipManager mPipManager = PipManager.getInstance();
private final WindowManager mWindowManager;
+ private final SystemServicesProxy mSystemServicesProxy;
private View mOverlayView;
private PipRecentsControlsView mPipControlsView;
private View mRecentsView;
+ private boolean mTalkBackEnabled;
- private final LayoutParams mPipRecentsControlsViewLayoutParams;
- private final LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
+ private LayoutParams mPipRecentsControlsViewLayoutParams;
+ private LayoutParams mPipRecentsControlsViewFocusedLayoutParams;
+ private boolean mHasFocusableInRecents;
private boolean mIsPipRecentsOverlayShown;
private boolean mIsRecentsShown;
private boolean mIsPipFocusedInRecent;
@@ -72,18 +80,7 @@
PipRecentsOverlayManager(Context context) {
mWindowManager = (WindowManager) context.getSystemService(WindowManager.class);
-
- mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- LayoutParams.TYPE_SYSTEM_DIALOG,
- LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
- PixelFormat.TRANSLUCENT);
- mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- LayoutParams.TYPE_SYSTEM_DIALOG,
- 0,
- PixelFormat.TRANSLUCENT);
-
+ mSystemServicesProxy = SystemServicesProxy.getInstance(context);
initViews(context);
}
@@ -101,6 +98,20 @@
}
}
});
+
+ mOverlayView.measure(UNSPECIFIED, UNSPECIFIED);
+ mPipRecentsControlsViewLayoutParams = new WindowManager.LayoutParams(
+ mOverlayView.getMeasuredWidth(), mOverlayView.getMeasuredHeight(),
+ LayoutParams.TYPE_SYSTEM_DIALOG,
+ LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ mPipRecentsControlsViewLayoutParams.gravity = TOP | CENTER_HORIZONTAL;
+ mPipRecentsControlsViewFocusedLayoutParams = new WindowManager.LayoutParams(
+ mOverlayView.getMeasuredWidth(), mOverlayView.getMeasuredHeight(),
+ LayoutParams.TYPE_SYSTEM_DIALOG,
+ 0,
+ PixelFormat.TRANSLUCENT);
+ mPipRecentsControlsViewFocusedLayoutParams.gravity = TOP | CENTER_HORIZONTAL;
}
/**
@@ -111,9 +122,10 @@
if (mIsPipRecentsOverlayShown) {
return;
}
+ mTalkBackEnabled = mSystemServicesProxy.isTouchExplorationEnabled();
+ mRecentsView.setVisibility(mTalkBackEnabled ? View.VISIBLE : View.GONE);
mIsPipRecentsOverlayShown = true;
mIsPipFocusedInRecent = true;
- mPipControlsView.reset();
mWindowManager.addView(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
}
@@ -126,50 +138,46 @@
return;
}
mWindowManager.removeView(mOverlayView);
+ // Resets the controls view when its removed.
+ // If not, changing focus in reset will be show animation when Recents is resumed.
+ mPipControlsView.reset();
mIsPipRecentsOverlayShown = false;
}
/**
* Request focus to the PIP Recents overlay.
- * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
- * is focused.
* This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
- * @param allowRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
+ * @param hasFocusableInRecents {@code true} if Recents can have focus. (i.e. Has a recent task)
*/
- public void requestFocus(boolean allowRecentsFocusable) {
- mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
+ public void requestFocus(boolean hasFocusableInRecents) {
+ mHasFocusableInRecents = hasFocusableInRecents;
if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || mIsPipFocusedInRecent
|| !mPipManager.isPipShown()) {
return;
}
mIsPipFocusedInRecent = true;
- mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
-
- mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
- mPipControlsView.requestFocus();
- mPipControlsView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
mPipControlsView.startFocusGainAnimation();
+ mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
+ mPipManager.resizePinnedStack(STATE_PIP_RECENTS_FOCUSED);
+ if (mTalkBackEnabled) {
+ mPipControlsView.requestFocus();
+ mPipControlsView.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
}
/**
* Request focus to the PIP Recents overlay.
- * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
- * is focused.
- * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
*/
public void clearFocus() {
if (!mIsPipRecentsOverlayShown || !mIsRecentsShown || !mIsPipFocusedInRecent
- || !mPipManager.isPipShown()) {
+ || !mPipManager.isPipShown() || !mHasFocusableInRecents) {
return;
}
- if (!mRecentsView.hasFocus()) {
- // Let mRecentsView's focus listener handle clearFocus().
- mRecentsView.requestFocus();
- }
mIsPipFocusedInRecent = false;
- mPipManager.resizePinnedStack(STATE_PIP_RECENTS);
+ mPipControlsView.startFocusLossAnimation();
mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewLayoutParams);
- mPipControlsView.startFocusLoseAnimation();
+ mPipManager.resizePinnedStack(STATE_PIP_RECENTS);
if (mCallback != null) {
mCallback.onRecentsFocused();
}
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index 08257b6..a3d27ce 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -20,6 +20,7 @@
package="com.android.vpndialogs">
<uses-permission android:name="android.permission.CONTROL_VPN" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<application android:label="VpnDialogs"
android:allowBackup="false" >
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index badc31e..72ce9c4 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -60,6 +60,12 @@
finish();
return;
}
+ final String alwaysOnVpnPackage = getAlwaysOnVpnPackage();
+ // Can't prepare new vpn app when another vpn is always-on
+ if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
+ finish();
+ return;
+ }
View view = View.inflate(this, R.layout.confirm, null);
((TextView) view.findViewById(R.id.warning)).setText(
Html.fromHtml(getString(R.string.warning, getVpnLabel()),
@@ -76,6 +82,16 @@
button.setFilterTouchesWhenObscured(true);
}
+ private String getAlwaysOnVpnPackage() {
+ try {
+ return mService.getAlwaysOnVpnPackage(UserHandle.myUserId());
+ } catch (RemoteException e) {
+ Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e);
+ // Fallback to null to show the dialog
+ return null;
+ }
+ }
+
private boolean prepareVpn() {
try {
return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index fee8ed9..3a14ad2 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2212,6 +2212,13 @@
// Notification group expansion state toggled by the expand affordance.
ACTION_NOTIFICATION_GROUP_EXPANDER = 408;
+
+ // Notification expansion state toggled by the expand gesture.
+ ACTION_NOTIFICATION_GESTURE_EXPANDER = 409;
+
+ // Notification group expansion state toggled by the expand gesture.
+ ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER = 410;
+
// ---- End N Constants, all N constants go above this line ----
// ------- Begin N App Disambig Shade -----
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 69960c7..bb966f7 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -542,6 +542,8 @@
"mms_temp_app_whitelist_duration";
private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION =
"sms_temp_app_whitelist_duration";
+ private static final String KEY_NOTIFICATION_WHITELIST_DURATION =
+ "notification_whitelist_duration";
/**
* This is the time, after becoming inactive, that we go in to the first
@@ -752,6 +754,14 @@
*/
public long SMS_TEMP_APP_WHITELIST_DURATION;
+ /**
+ * Amount of time we would like to whitelist an app that is handling a
+ * {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
+ * @see Settings.Global#DEVICE_IDLE_CONSTANTS
+ * @see #KEY_NOTIFICATION_WHITELIST_DURATION
+ */
+ public long NOTIFICATION_WHITELIST_DURATION;
+
private final ContentResolver mResolver;
private final boolean mHasWatch;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -842,6 +852,8 @@
KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L);
SMS_TEMP_APP_WHITELIST_DURATION = mParser.getLong(
KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
+ NOTIFICATION_WHITELIST_DURATION = mParser.getLong(
+ KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
}
}
@@ -945,6 +957,10 @@
pw.print(" "); pw.print(KEY_SMS_TEMP_APP_WHITELIST_DURATION); pw.print("=");
TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw);
pw.println();
+
+ pw.print(" "); pw.print(KEY_NOTIFICATION_WHITELIST_DURATION); pw.print("=");
+ TimeUtils.formatDuration(NOTIFICATION_WHITELIST_DURATION, pw);
+ pw.println();
}
}
@@ -1252,6 +1268,10 @@
addPowerSaveTempWhitelistAppDirectInternal(0, appId, duration, sync, reason);
}
+ public long getNotificationWhitelistDuration() {
+ return mConstants.NOTIFICATION_WHITELIST_DURATION;
+ }
+
public void setNetworkPolicyTempWhitelistCallback(Runnable callback) {
setNetworkPolicyTempWhitelistCallbackInternal(callback);
}
@@ -1632,7 +1652,7 @@
}
entry.first.value = timeNow + duration;
if (DEBUG) {
- Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist");
+ Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist. New entry: " + newEntry);
}
if (newEntry) {
// No pending timeout for the app id, post a delayed message
@@ -1665,12 +1685,18 @@
}
private void postTempActiveTimeoutMessage(int uid, long delay) {
+ if (DEBUG) {
+ Slog.d(TAG, "postTempActiveTimeoutMessage: uid=" + uid + ", delay=" + delay);
+ }
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TEMP_APP_WHITELIST_TIMEOUT, uid, 0),
delay);
}
void checkTempAppWhitelistTimeout(int uid) {
final long timeNow = SystemClock.elapsedRealtime();
+ if (DEBUG) {
+ Slog.d(TAG, "checkTempAppWhitelistTimeout: uid=" + uid + ", timeNow=" + timeNow);
+ }
synchronized (this) {
Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(uid);
if (entry == null) {
@@ -1694,6 +1720,9 @@
}
} else {
// Need more time
+ if (DEBUG) {
+ Slog.d(TAG, "Time to remove UID " + uid + ": " + entry.first.value);
+ }
postTempActiveTimeoutMessage(uid, entry.first.value - timeNow);
}
}
@@ -2514,6 +2543,8 @@
pw.println(" Print currently whitelisted apps.");
pw.println(" whitelist [package ...]");
pw.println(" Add (prefix with +) or remove (prefix with -) packages.");
+ pw.println(" tempwhitelist");
+ pw.println(" Print packages that are temporarily whitelisted.");
pw.println(" tempwhitelist [-u] [package ..]");
pw.println(" Temporarily place packages in whitelist for 10 seconds.");
}
@@ -2817,8 +2848,7 @@
pw.println("Failed: " + re);
}
} else {
- pw.println("At least one package name must be specified");
- return -1;
+ dumpTempWhitelistSchedule(pw, false);
}
} else {
return shell.handleDefaultCommands(cmd);
@@ -2943,20 +2973,8 @@
pw.println();
}
}
- size = mTempWhitelistAppIdEndTimes.size();
- if (size > 0) {
- pw.println(" Temp whitelist schedule:");
- final long timeNow = SystemClock.elapsedRealtime();
- for (int i = 0; i < size; i++) {
- pw.print(" UID=");
- pw.print(mTempWhitelistAppIdEndTimes.keyAt(i));
- pw.print(": ");
- Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.valueAt(i);
- TimeUtils.formatDuration(entry.first.value, timeNow, pw);
- pw.print(" - ");
- pw.println(entry.second);
- }
- }
+ dumpTempWhitelistSchedule(pw, true);
+
size = mTempWhitelistAppIdArray != null ? mTempWhitelistAppIdArray.length : 0;
if (size > 0) {
pw.println(" Temp whitelist app ids:");
@@ -2968,7 +2986,7 @@
}
pw.print(" mLightEnabled="); pw.print(mLightEnabled);
- pw.print(" mDeepEnabled="); pw.println(mDeepEnabled);
+ pw.print(" mDeepEnabled="); pw.println(mDeepEnabled);
pw.print(" mForceIdle="); pw.println(mForceIdle);
pw.print(" mMotionSensor="); pw.println(mMotionSensor);
pw.print(" mCurDisplay="); pw.println(mCurDisplay);
@@ -3040,4 +3058,26 @@
}
}
}
-}
+
+ void dumpTempWhitelistSchedule(PrintWriter pw, boolean printTitle) {
+ final int size = mTempWhitelistAppIdEndTimes.size();
+ if (size > 0) {
+ String prefix = "";
+ if (printTitle) {
+ pw.println(" Temp whitelist schedule:");
+ prefix = " ";
+ }
+ final long timeNow = SystemClock.elapsedRealtime();
+ for (int i = 0; i < size; i++) {
+ pw.print(prefix);
+ pw.print("UID=");
+ pw.print(mTempWhitelistAppIdEndTimes.keyAt(i));
+ pw.print(": ");
+ Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.valueAt(i);
+ TimeUtils.formatDuration(entry.first.value, timeNow, pw);
+ pw.print(" - ");
+ pw.println(entry.second);
+ }
+ }
+ }
+ }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 727bf5c..5d8fe7c 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -159,6 +159,7 @@
static final int MSG_BIND_INPUT = 1010;
static final int MSG_SHOW_SOFT_INPUT = 1020;
static final int MSG_HIDE_SOFT_INPUT = 1030;
+ static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
static final int MSG_ATTACH_TOKEN = 1040;
static final int MSG_CREATE_SESSION = 1050;
@@ -2846,6 +2847,11 @@
}
args.recycle();
return true;
+ case MSG_HIDE_CURRENT_INPUT_METHOD:
+ synchronized (mMethodMap) {
+ hideCurrentInputLocked(0, null);
+ }
+ return true;
case MSG_ATTACH_TOKEN:
args = (SomeArgs)msg.obj;
try {
@@ -3880,6 +3886,12 @@
mHandler.sendMessage(mHandler.obtainMessage(MSG_SWITCH_IME,
forwardDirection ? 1 : 0, 0));
}
+
+ @Override
+ public void hideCurrentInputMethod() {
+ mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
+ mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
+ }
}
private static String imeWindowStatusToString(final int imeWindowVis) {
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 2a78f90..3745e0b 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -17,12 +17,10 @@
package com.android.server;
import android.Manifest.permission;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.INetworkScoreCache;
@@ -33,7 +31,6 @@
import android.net.ScoredNetwork;
import android.os.Binder;
import android.os.IBinder;
-import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
@@ -42,6 +39,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -62,39 +60,75 @@
private final Context mContext;
private final Map<Integer, INetworkScoreCache> mScoreCaches;
- /** Lock used to update mReceiver when scorer package changes occur. */
- private final Object mReceiverLock = new Object[0];
+ /** Lock used to update mPackageMonitor when scorer package changes occur. */
+ private final Object mPackageMonitorLock = new Object[0];
- /** Clears scores when the active scorer package is no longer valid. */
- @GuardedBy("mReceiverLock")
- private ScorerChangedReceiver mReceiver;
+ @GuardedBy("mPackageMonitorLock")
+ private NetworkScorerPackageMonitor mPackageMonitor;
private ScoringServiceConnection mServiceConnection;
- private class ScorerChangedReceiver extends BroadcastReceiver {
+ /**
+ * Clears scores when the active scorer package is no longer valid and
+ * manages the service connection.
+ */
+ private class NetworkScorerPackageMonitor extends PackageMonitor {
final String mRegisteredPackage;
- ScorerChangedReceiver(String packageName) {
- mRegisteredPackage = packageName;
+ private NetworkScorerPackageMonitor(String mRegisteredPackage) {
+ this.mRegisteredPackage = mRegisteredPackage;
}
@Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
- || Intent.ACTION_PACKAGE_REPLACED.equals(action)
- || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
- NetworkScorerAppData activeScorer =
+ public void onPackageAdded(String packageName, int uid) {
+ evaluateBinding(packageName, true /* forceUnbind */);
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ evaluateBinding(packageName, true /* forceUnbind */);
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ evaluateBinding(packageName, false /* forceUnbind */);
+ }
+
+ @Override
+ public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+ if (doit) { // "doit" means the force stop happened instead of just being queried for.
+ for (String packageName : packages) {
+ evaluateBinding(packageName, true /* forceUnbind */);
+ }
+ }
+ return super.onHandleForceStop(intent, packages, uid, doit);
+ }
+
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ evaluateBinding(packageName, true /* forceUnbind */);
+ }
+
+ private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
+ if (mRegisteredPackage.equals(scorerPackageName)) {
+ if (DBG) {
+ Log.d(TAG, "Evaluating binding for: " + scorerPackageName
+ + ", forceUnbind=" + forceUnbind);
+ }
+ final NetworkScorerAppData activeScorer =
NetworkScorerAppManager.getActiveScorer(mContext);
if (activeScorer == null) {
- // Package change has invalidated a scorer.
+ // Package change has invalidated a scorer, this will also unbind any service
+ // connection.
Log.i(TAG, "Package " + mRegisteredPackage +
" is no longer valid, disabling scoring.");
setScorerInternal(null);
} else if (activeScorer.mScoringServiceClassName == null) {
// The scoring service is not available, make sure it's unbound.
unbindFromScoringServiceIfNeeded();
- } else {
- // The scoring service may have changed or been added.
+ } else { // The scoring service changed in some way.
+ if (forceUnbind) {
+ unbindFromScoringServiceIfNeeded();
+ }
bindToScoringServiceIfNeeded(activeScorer);
}
}
@@ -121,7 +155,7 @@
Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
}
- registerPackageReceiverIfNeeded();
+ registerPackageMonitorIfNeeded();
}
/** Called when the system is ready for us to start third-party code. */
@@ -130,33 +164,29 @@
bindToScoringServiceIfNeeded();
}
- private void registerPackageReceiverIfNeeded() {
- if (DBG) Log.d(TAG, "registerPackageReceiverIfNeeded");
+ private void registerPackageMonitorIfNeeded() {
+ if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
- synchronized (mReceiverLock) {
- // Unregister the receiver if the current scorer has changed since last registration.
- if (mReceiver != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Unregistering receiver for " + mReceiver.mRegisteredPackage);
+ synchronized (mPackageMonitorLock) {
+ // Unregister the current monitor if needed.
+ if (mPackageMonitor != null) {
+ if (DBG) {
+ Log.d(TAG, "Unregistering package monitor for "
+ + mPackageMonitor.mRegisteredPackage);
}
- mContext.unregisterReceiver(mReceiver);
- mReceiver = null;
+ mPackageMonitor.unregister();
+ mPackageMonitor = null;
}
- // Register receiver if a scorer is active.
+ // Create and register the monitor if a scorer is active.
if (scorer != null) {
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
- filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
- filter.addDataScheme("package");
- filter.addDataSchemeSpecificPart(scorer.mPackageName,
- PatternMatcher.PATTERN_LITERAL);
- mReceiver = new ScorerChangedReceiver(scorer.mPackageName);
+ mPackageMonitor = new NetworkScorerPackageMonitor(scorer.mPackageName);
// TODO: Need to update when we support per-user scorers. http://b/23422763
- mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter, null, null);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Registered receiver for " + scorer.mPackageName);
+ mPackageMonitor.register(mContext, null /* thread */, UserHandle.SYSTEM,
+ false /* externalStorage */);
+ if (DBG) {
+ Log.d(TAG, "Registered package monitor for "
+ + mPackageMonitor.mRegisteredPackage);
}
}
}
@@ -299,7 +329,7 @@
// will be made to bind to the new scorer.
bindToScoringServiceIfNeeded();
if (result) { // new scorer successfully set
- registerPackageReceiverIfNeeded();
+ registerPackageMonitorIfNeeded();
Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED);
if (prevScorer != null) { // Directly notify the old scorer.
@@ -391,20 +421,24 @@
private static class ScoringServiceConnection implements ServiceConnection {
private final ComponentName mComponentName;
private boolean mBound = false;
+ private boolean mConnected = false;
ScoringServiceConnection(ComponentName componentName) {
mComponentName = componentName;
}
void connect(Context context) {
- disconnect(context);
- Intent service = new Intent();
- service.setComponent(mComponentName);
- mBound = context.bindServiceAsUser(service, this,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- UserHandle.SYSTEM);
if (!mBound) {
- Log.w(TAG, "Bind call failed for " + service);
+ Intent service = new Intent();
+ service.setComponent(mComponentName);
+ mBound = context.bindServiceAsUser(service, this,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ UserHandle.SYSTEM);
+ if (!mBound) {
+ Log.w(TAG, "Bind call failed for " + service);
+ } else {
+ if (DBG) Log.d(TAG, "ScoringServiceConnection bound.");
+ }
}
}
@@ -413,6 +447,7 @@
if (mBound) {
mBound = false;
context.unbindService(this);
+ if (DBG) Log.d(TAG, "ScoringServiceConnection unbound.");
}
} catch (RuntimeException e) {
Log.e(TAG, "Unbind failed.", e);
@@ -422,15 +457,20 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString());
+ mConnected = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
- if (DBG) Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
+ if (DBG) {
+ Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString());
+ }
+ mConnected = false;
}
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound);
+ writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound
+ + ", connected: " + mConnected);
}
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c24123a..6a8c8b0 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -831,8 +831,9 @@
int clientLabel = 0;
PendingIntent clientIntent = null;
+ final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID;
- if (callerApp.info.uid == Process.SYSTEM_UID) {
+ if (isCallerSystem) {
// Hacky kind of thing -- allow system stuff to tell us
// what they are, so we can report this elsewhere for
// others to know why certain services are running.
@@ -854,6 +855,12 @@
"BIND_TREAT_LIKE_ACTIVITY");
}
+ if ((flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0 && !isCallerSystem) {
+ throw new SecurityException(
+ "Non-system caller " + caller + " (pid=" + Binder.getCallingPid()
+ + ") set BIND_ALLOW_WHITELIST_MANAGEMENT when binding service " + service);
+ }
+
final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
@@ -1124,6 +1131,11 @@
}
if (r.binding.service.app != null) {
+ if (r.binding.service.app.whitelistManager) {
+ // Must reset flag here because on computeOomAdjLocked() the service
+ // connection will be gone...
+ r.binding.service.app.whitelistManager = false;
+ }
// This could have made the service less important.
if ((r.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
r.binding.service.app.treatLikeActivity = true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index f2bf4f9..43bb5ee 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -90,6 +90,7 @@
static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
+ static final boolean DEBUG_WHITELISTS = DEBUG_ALL || false;
static final String POSTFIX_ADD_REMOVE = (APPEND_CATEGORY_NAME) ? "_AddRemove" : "";
static final String POSTFIX_APP = (APPEND_CATEGORY_NAME) ? "_App" : "";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 780f632..3da22d6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -321,6 +321,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
@@ -7135,6 +7136,41 @@
}
}
+ /**
+ * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
+ *
+ * <p>{@code callerUid} must be allowed to request such whitelist by calling
+ * {@link #addTempPowerSaveWhitelistGrantorUid(int)}.
+ */
+ void tempWhitelistAppForPowerSave(int callerPid, int callerUid, int targetUid, long duration) {
+ if (DEBUG_WHITELISTS) {
+ Slog.d(TAG, "tempWhitelistAppForPowerSave(" + callerPid + ", " + callerUid + ", "
+ + targetUid + ", " + duration + ")");
+ }
+ synchronized (mPidsSelfLocked) {
+ final ProcessRecord pr = mPidsSelfLocked.get(callerPid);
+ if (pr == null) {
+ Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid " + callerPid);
+ return;
+ }
+ if (!pr.whitelistManager) {
+ if (DEBUG_WHITELISTS) {
+ Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid + ": pid "
+ + callerPid + " is not allowed");
+ }
+ return;
+ }
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(targetUid, duration,
+ true, "pe from uid:" + callerUid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public void cancelIntentSender(IIntentSender sender) {
if (!(sender instanceof PendingIntentRecord)) {
@@ -18823,7 +18859,7 @@
for (int j = 0; j < activitiesSize; j++) {
final ActivityRecord r = app.activities.get(j);
if (r.app != app) {
- Log.wtf(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ " instead of expected " + app);
if (r.app == null || (r.app.uid == app.uid)) {
// Only fix things up when they look sane
@@ -19025,6 +19061,9 @@
}
}
}
+
+ app.whitelistManager = false;
+
for (int conni = s.connections.size()-1;
conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
@@ -19043,6 +19082,10 @@
// Binding to ourself is not interesting.
continue;
}
+ if ((cr.flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
+ app.whitelistManager = true;
+ }
+
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
ProcessRecord client = cr.binding.client;
int clientAdj = computeOomAdjLocked(client, cachedAdj,
@@ -21319,6 +21362,15 @@
}
}
}
+
+ @Override
+ public void setPendingIntentWhitelistDuration(IIntentSender target, long duration) {
+ if (!(target instanceof PendingIntentRecord)) {
+ Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
+ return;
+ }
+ ((PendingIntentRecord) target).setWhitelistDuration(duration);
+ }
}
private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index ddfab4d..e37feb2 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -18,13 +18,17 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
import android.text.BidiFormatter;
import android.util.Slog;
import android.view.LayoutInflater;
@@ -43,6 +47,7 @@
private final AppErrorResult mResult;
private final ProcessRecord mProc;
private final boolean mRepeating;
+ private final boolean mForeground;
private CharSequence mName;
@@ -54,9 +59,9 @@
static final int FORCE_QUIT = 1;
static final int FORCE_QUIT_AND_REPORT = 2;
static final int RESTART = 3;
- static final int RESET = 4;
static final int MUTE = 5;
static final int TIMEOUT = 6;
+ static final int CANCEL = 7;
// 5-minute timeout, then we automatically dismiss the crash dialog
static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
@@ -69,6 +74,7 @@
mProc = data.proc;
mResult = data.result;
mRepeating = data.repeating;
+ mForeground = data.task != null;
BidiFormatter bidi = BidiFormatter.getInstance();
if ((mProc.pkgList.size() == 1) &&
@@ -86,7 +92,8 @@
bidi.unicodeWrap(mName.toString())));
}
- setCancelable(false);
+ setCancelable(true);
+ setCancelMessage(mHandler.obtainMessage(CANCEL));
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle("Application Error: " + mProc.info.processName);
@@ -111,25 +118,41 @@
LayoutInflater.from(context).inflate(
com.android.internal.R.layout.app_error_dialog, frame, true);
+ boolean hasRestart = !mRepeating && mForeground;
+ final boolean hasReceiver = mProc.errorReportReceiver != null;
+
final TextView restart = (TextView) findViewById(com.android.internal.R.id.aerr_restart);
restart.setOnClickListener(this);
- restart.setVisibility(!mRepeating ? View.VISIBLE : View.GONE);
- final TextView reset = (TextView) findViewById(com.android.internal.R.id.aerr_reset);
- reset.setOnClickListener(this);
- reset.setVisibility(mRepeating ? View.VISIBLE : View.GONE);
+ restart.setVisibility(hasRestart ? View.VISIBLE : View.GONE);
final TextView report = (TextView) findViewById(com.android.internal.R.id.aerr_report);
report.setOnClickListener(this);
- final boolean hasReceiver = mProc.errorReportReceiver != null;
report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE);
final TextView close = (TextView) findViewById(com.android.internal.R.id.aerr_close);
+ close.setVisibility(!hasRestart ? View.VISIBLE : View.GONE);
close.setOnClickListener(this);
+
+ boolean showMute = !IS_USER_BUILD && Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
final TextView mute = (TextView) findViewById(com.android.internal.R.id.aerr_mute);
mute.setOnClickListener(this);
- mute.setVisibility(!IS_USER_BUILD ? View.VISIBLE : View.GONE);
+ mute.setVisibility(showMute ? View.VISIBLE : View.GONE);
findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE);
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ getContext().registerReceiver(mReceiver,
+ new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ getContext().unregisterReceiver(mReceiver);
+ }
+
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
final int result = msg.what;
@@ -163,9 +186,6 @@
case com.android.internal.R.id.aerr_restart:
mHandler.obtainMessage(RESTART).sendToTarget();
break;
- case com.android.internal.R.id.aerr_reset:
- mHandler.obtainMessage(RESET).sendToTarget();
- break;
case com.android.internal.R.id.aerr_report:
mHandler.obtainMessage(FORCE_QUIT_AND_REPORT).sendToTarget();
break;
@@ -180,6 +200,15 @@
}
}
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+ cancel();
+ }
+ }
+ };
+
static class Data {
AppErrorResult result;
TaskRecord task;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 3ed9969..49106f4 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -370,38 +370,9 @@
Intent appErrorIntent = null;
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
- if (res == AppErrorDialog.TIMEOUT) {
+ if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
res = AppErrorDialog.FORCE_QUIT;
}
- if (res == AppErrorDialog.RESET) {
- String[] packageList = r.getPackageList();
- if (packageList != null) {
- PackageManager pm = mContext.getPackageManager();
- final Semaphore s = new Semaphore(0);
- for (int i = 0; i < packageList.length; i++) {
- if (i < packageList.length - 1) {
- pm.deleteApplicationCacheFiles(packageList[i], null);
- } else {
- pm.deleteApplicationCacheFiles(packageList[i],
- new IPackageDataObserver.Stub() {
- @Override
- public void onRemoveCompleted(String packageName,
- boolean succeeded) {
- s.release();
- }
- });
-
- // Wait until cache has been cleared before we restart.
- try {
- s.acquire();
- } catch (InterruptedException e) {
- }
- }
- }
- }
- // If there was nothing to reset, just restart;
- res = AppErrorDialog.RESTART;
- }
synchronized (mService) {
if (res == AppErrorDialog.MUTE) {
stopReportingCrashesLocked(r);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 1f8d26b..c1ff4dd 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -47,6 +47,7 @@
final WeakReference<PendingIntentRecord> ref;
boolean sent = false;
boolean canceled = false;
+ private long whitelistDuration = 0;
String stringName;
String lastTagPrefix;
@@ -66,9 +67,9 @@
final int flags;
final int hashCode;
final int userId;
-
+
private static final int ODD_PRIME_NUMBER = 37;
-
+
Key(int _t, String _p, ActivityRecord _a, String _w,
int _r, Intent[] _i, String[] _it, int _f, Bundle _o, int _userId) {
type = _t;
@@ -106,7 +107,7 @@
//Slog.i(ActivityManagerService.TAG, this + " hashCode=0x"
// + Integer.toHexString(hashCode));
}
-
+
public boolean equals(Object otherObj) {
if (otherObj == null) {
return false;
@@ -198,6 +199,11 @@
ref = new WeakReference<PendingIntentRecord>(this);
}
+ void setWhitelistDuration(long duration) {
+ this.whitelistDuration = duration;
+ this.stringName = null;
+ }
+
public void send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
String requiredPermission, Bundle options) {
sendInner(code, intent, resolvedType, finishedReceiver,
@@ -216,6 +222,14 @@
if (intent != null) intent.setDefusable(true);
if (options != null) options.setDefusable(true);
+ if (whitelistDuration > 0 && !canceled) {
+ // Must call before acquiring the lock. It's possible the method return before sending
+ // the intent due to some validations inside the lock, in which case the UID shouldn't
+ // be whitelisted, but since the whitelist is temporary, that would be ok.
+ owner.tempWhitelistAppForPowerSave(Binder.getCallingPid(), Binder.getCallingUid(), uid,
+ whitelistDuration);
+ }
+
synchronized (owner) {
final ActivityContainer activityContainer = (ActivityContainer)container;
if (activityContainer != null && activityContainer.mParentActivity != null &&
@@ -361,7 +375,7 @@
}
}
}
-
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("uid="); pw.print(uid);
pw.print(" packageName="); pw.print(key.packageName);
@@ -383,6 +397,7 @@
pw.print(prefix); pw.print("sent="); pw.print(sent);
pw.print(" canceled="); pw.println(canceled);
}
+ pw.print(prefix); pw.println("whitelistDuration="); pw.println(whitelistDuration);
}
public String toString() {
@@ -396,6 +411,9 @@
sb.append(key.packageName);
sb.append(' ');
sb.append(key.typeName());
+ if (whitelistDuration > 0) {
+ sb.append( " (whitelistDuration: ").append(whitelistDuration).append("ms)");
+ }
sb.append('}');
return stringName = sb.toString();
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index da18f32..691fd2a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -194,6 +194,8 @@
// Process is currently hosting a backup agent for backup or restore
public boolean inFullBackup;
+ // App is allowed to manage whitelists such as temporary Power Save mode whitelist.
+ boolean whitelistManager;
void dump(PrintWriter pw, String prefix) {
final long now = SystemClock.uptimeMillis();
@@ -376,6 +378,9 @@
}
pw.println();
}
+ if (whitelistManager) {
+ pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
+ }
if (activities.size() > 0) {
pw.print(prefix); pw.println("Activities:");
for (int i=0; i<activities.size(); i++) {
diff --git a/services/core/java/com/android/server/am/RetailDemoModeService.java b/services/core/java/com/android/server/am/RetailDemoModeService.java
index 5929794..a1106f5 100644
--- a/services/core/java/com/android/server/am/RetailDemoModeService.java
+++ b/services/core/java/com/android/server/am/RetailDemoModeService.java
@@ -17,6 +17,9 @@
package com.android.server.am;
import android.app.ActivityManagerNative;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -36,6 +39,7 @@
import android.util.Slog;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.R;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.pm.UserManagerService;
@@ -47,15 +51,18 @@
private static final String TAG = RetailDemoModeService.class.getSimpleName();
private static final String DEMO_USER_NAME = "Demo";
+ private static final String ACTION_RESET_DEMO = "com.android.server.am.ACTION_RESET_DEMO";
private static final long SCREEN_WAKEUP_DELAY = 5000;
private ActivityManagerService mAms;
private UserManagerService mUms;
+ private NotificationManager mNm;
private PowerManager mPm;
private PowerManager.WakeLock mWakeLock;
private Handler mHandler;
private ServiceThread mHandlerThread;
+ private PendingIntent mResetDemoPendingIntent;
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -75,6 +82,9 @@
}
}, SCREEN_WAKEUP_DELAY);
break;
+ case ACTION_RESET_DEMO:
+ createAndSwitchToDemoUser();
+ break;
}
}
};
@@ -83,6 +93,26 @@
super(context);
}
+ private Notification createResetNotification() {
+ return new Notification.Builder(getContext())
+ .setContentTitle(getContext().getString(R.string.reset_retail_demo_mode_title))
+ .setContentText(getContext().getString(R.string.reset_retail_demo_mode_text))
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.platlogo)
+ .setShowWhen(false)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setContentIntent(getResetDemoPendingIntent())
+ .build();
+ }
+
+ private PendingIntent getResetDemoPendingIntent() {
+ if (mResetDemoPendingIntent == null) {
+ Intent intent = new Intent(ACTION_RESET_DEMO);
+ mResetDemoPendingIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
+ }
+ return mResetDemoPendingIntent;
+ }
+
private void createAndSwitchToDemoUser() {
if (DEBUG) {
Slog.d(TAG, "Switching to a new demo user");
@@ -162,7 +192,8 @@
private void registerBroadcastReceiver() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
- getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ filter.addAction(ACTION_RESET_DEMO);
+ getContext().registerReceiver(mBroadcastReceiver, filter);
}
@Override
@@ -184,6 +215,8 @@
mPm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
mWakeLock = mPm
.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
+ mNm = NotificationManager.from(getContext());
+
if (UserManager.isDeviceInDemoMode(getContext())) {
createAndSwitchToDemoUser();
}
@@ -208,5 +241,6 @@
if (!mWakeLock.isHeld()) {
mWakeLock.acquire();
}
+ mNm.notifyAsUser(TAG, 1, createResetNotification(), UserHandle.of(userId));
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2023173..ee85a57 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -18,7 +18,6 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.app.ActivityManager.FLAG_AND_UNLOCKING_OR_UNLOCKED;
import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
@@ -234,6 +233,7 @@
// but we might immediately step into RUNNING below if the user
// storage is already unlocked.
if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) {
+ getUserManagerInternal().setUserState(userId, uss.state);
Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
@@ -277,7 +277,7 @@
if (!StorageManager.isUserKeyUnlocked(userId)) return;
if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) {
- getUserManagerInternal().setUserUnlockingOrUnlocked(userId, true);
+ getUserManagerInternal().setUserState(userId, uss.state);
uss.mUnlockProgress.start();
// Prepare app storage before we go any further
@@ -308,7 +308,7 @@
if (!StorageManager.isUserKeyUnlocked(userId)) return;
if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) {
- getUserManagerInternal().setUserUnlockingOrUnlocked(userId, true);
+ getUserManagerInternal().setUserState(userId, uss.state);
uss.mUnlockProgress.finish();
// Dispatch unlocked to external apps
@@ -482,7 +482,7 @@
if (uss.state != UserState.STATE_STOPPING
&& uss.state != UserState.STATE_SHUTDOWN) {
uss.setState(UserState.STATE_STOPPING);
- getUserManagerInternal().setUserUnlockingOrUnlocked(userId, false);
+ getUserManagerInternal().setUserState(userId, uss.state);
updateStartedUserArrayLocked();
long ident = Binder.clearCallingIdentity();
@@ -544,7 +544,7 @@
}
uss.setState(UserState.STATE_SHUTDOWN);
}
- getUserManagerInternal().setUserUnlockingOrUnlocked(userId, false);
+ getUserManagerInternal().setUserState(userId, uss.state);
mService.mBatteryStatsService.noteEvent(
BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH,
@@ -573,6 +573,7 @@
stopped = true;
// User can no longer run.
mStartedUsers.remove(userId);
+ getUserManagerInternal().removeUserState(userId);
mUserLru.remove(Integer.valueOf(userId));
updateStartedUserArrayLocked();
@@ -780,7 +781,9 @@
// If the user we are switching to is not currently started, then
// we need to start it now.
if (mStartedUsers.get(userId) == null) {
- mStartedUsers.put(userId, new UserState(UserHandle.of(userId)));
+ UserState userState = new UserState(UserHandle.of(userId));
+ mStartedUsers.put(userId, userState);
+ getUserManagerInternal().setUserState(userId, userState.state);
updateStartedUserArrayLocked();
needStart = true;
}
@@ -814,15 +817,14 @@
// so we can just fairly silently bring the user back from
// the almost-dead.
uss.setState(uss.lastState);
- if (isUserRunningLocked(userId, FLAG_AND_UNLOCKING_OR_UNLOCKED)) {
- getUserManagerInternal().setUserUnlockingOrUnlocked(userId, true);
- }
+ getUserManagerInternal().setUserState(userId, uss.state);
updateStartedUserArrayLocked();
needStart = true;
} else if (uss.state == UserState.STATE_SHUTDOWN) {
// This means ACTION_SHUTDOWN has been sent, so we will
// need to treat this as a new boot of the user.
uss.setState(UserState.STATE_BOOTING);
+ getUserManagerInternal().setUserState(userId, uss.state);
updateStartedUserArrayLocked();
needStart = true;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 1698ab5..1bdb48a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -262,18 +262,37 @@
* It uses {@link VpnConfig#LEGACY_VPN} as its package name, and
* it can be revoked by itself.
*
- * @param oldPackage The package name of the old VPN application.
- * @param newPackage The package name of the new VPN application.
+ * Note: when we added VPN pre-consent in http://ag/522961 the names oldPackage
+ * and newPackage become misleading, because when an app is pre-consented, we
+ * actually prepare oldPackage, not newPackage.
+ *
+ * Their meanings actually are:
+ *
+ * - oldPackage non-null, newPackage null: App calling VpnService#prepare().
+ * - oldPackage null, newPackage non-null: ConfirmDialog calling prepareVpn().
+ * - oldPackage non-null, newPackage=LEGACY_VPN: Used internally to disconnect
+ * and revoke any current app VPN and re-prepare legacy vpn.
+ *
+ * TODO: Rename the variables - or split this method into two - and end this
+ * confusion.
+ *
+ * @param oldPackage The package name of the old VPN application
+ * @param newPackage The package name of the new VPN application
+ *
* @return true if the operation is succeeded.
*/
public synchronized boolean prepare(String oldPackage, String newPackage) {
- // Stop an existing always-on VPN from being dethroned by other apps.
- if (mAlwaysOn && !TextUtils.equals(mPackage, newPackage)) {
- return false;
- }
-
if (oldPackage != null) {
- if (getAppUid(oldPackage, mUserHandle) != mOwnerUID) {
+ // Stop an existing always-on VPN from being dethroned by other apps.
+ // TODO: Replace TextUtils.equals by isCurrentPreparedPackage when ConnectivityService
+ // can unset always-on after always-on package is uninstalled. Make sure when package
+ // is reinstalled, the consent dialog is not shown.
+ if (mAlwaysOn && !TextUtils.equals(mPackage, oldPackage)) {
+ return false;
+ }
+
+ // Package is not same or old package was reinstalled.
+ if (!isCurrentPreparedPackage(oldPackage)) {
// The package doesn't match. We return false (to obtain user consent) unless the
// user has already consented to that VPN package.
if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
@@ -291,17 +310,30 @@
// Return true if we do not need to revoke.
if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) &&
- getAppUid(newPackage, mUserHandle) == mOwnerUID)) {
+ isCurrentPreparedPackage(newPackage))) {
return true;
}
// Check that the caller is authorized.
enforceControlPermission();
+ // Stop an existing always-on VPN from being dethroned by other apps.
+ // TODO: Replace TextUtils.equals by isCurrentPreparedPackage when ConnectivityService
+ // can unset always-on after always-on package is uninstalled
+ if (mAlwaysOn && !TextUtils.equals(mPackage, newPackage)) {
+ return false;
+ }
+
prepareInternal(newPackage);
return true;
}
+ private boolean isCurrentPreparedPackage(String packageName) {
+ // We can't just check that packageName matches mPackage, because if the app was uninstalled
+ // and reinstalled it will no longer be prepared. Instead check the UID.
+ return getAppUid(packageName, mUserHandle) == mOwnerUID;
+ }
+
/** Prepare the VPN for the given package. Does not perform permission checks. */
private void prepareInternal(String newPackage) {
long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 491ee18..1b8eccb 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -16,6 +16,9 @@
package com.android.server.job;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -41,6 +44,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
@@ -217,7 +221,41 @@
if (DEBUG) {
Slog.d(TAG, "Receieved: " + intent.getAction());
}
- if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
+ // Purge the app's jobs if the whole package was just disabled. When this is
+ // the case the component name will be a bare package name.
+ final String pkgName = getPackageName(intent);
+ final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (pkgName != null && pkgUid != -1) {
+ final String[] changedComponents = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ if (changedComponents != null) {
+ for (String component : changedComponents) {
+ if (component.equals(pkgName)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Package state change: " + pkgName);
+ }
+ try {
+ final int userId = UserHandle.getUserId(pkgUid);
+ IPackageManager pm = AppGlobals.getPackageManager();
+ final int state = pm.getApplicationEnabledSetting(pkgName, userId);
+ if (state == COMPONENT_ENABLED_STATE_DISABLED
+ || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing jobs for package " + pkgName
+ + " in user " + userId);
+ }
+ cancelJobsForUid(pkgUid, true);
+ }
+ } catch (RemoteException e) { /* cannot happen */ }
+ break;
+ }
+ }
+ }
+ } else {
+ Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
+ }
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
// If this is an outright uninstall rather than the first half of an
// app update sequence, cancel the jobs associated with the app.
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -237,6 +275,12 @@
}
};
+ private String getPackageName(Intent intent) {
+ Uri uri = intent.getData();
+ String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+ return pkg;
+ }
+
final private IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
updateUidState(uid, procState);
@@ -520,7 +564,9 @@
public void onBootPhase(int phase) {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
// Register br for package removals and user removals.
- final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 53c5a6d..dc85dd7 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -16,6 +16,10 @@
package com.android.server.notification;
+import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.PendingIntent;
@@ -43,7 +47,6 @@
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -56,7 +59,6 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
@@ -681,7 +683,7 @@
};
if (!mContext.bindServiceAsUser(intent,
serviceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ BIND_AUTO_CREATE | BIND_FOREGROUND_SERVICE | BIND_ALLOW_WHITELIST_MANAGEMENT,
new UserHandle(userid))) {
mServicesBinding.remove(servicesBindingTag);
Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cf4669d..b5a8bf3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -40,11 +40,13 @@
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
+
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -56,6 +58,7 @@
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
+import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.app.backup.BackupManager;
import android.app.usage.UsageEvents;
@@ -64,6 +67,7 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -90,6 +94,7 @@
import android.os.IInterface;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -127,6 +132,7 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
+import com.android.server.DeviceIdleController;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -2542,6 +2548,8 @@
+ " id=" + id + " notification=" + notification);
}
+ markAsSentFromNotification(notification);
+
// Sanitize inputs
notification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,
Notification.PRIORITY_MAX);
@@ -2556,6 +2564,63 @@
idOut[0] = id;
}
+ private static void markAsSentFromNotification(Notification notification) {
+ final ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
+ final long duration = LocalServices.getService(DeviceIdleController.LocalService.class)
+ .getNotificationWhitelistDuration();
+
+ int size = 0;
+ if (notification.contentIntent != null) {
+ am.setPendingIntentWhitelistDuration(notification.contentIntent.getTarget(), duration);
+ }
+ if (notification.deleteIntent != null) {
+ am.setPendingIntentWhitelistDuration(notification.deleteIntent.getTarget(), duration);
+ }
+ if (notification.fullScreenIntent != null) {
+ am.setPendingIntentWhitelistDuration(notification.fullScreenIntent.getTarget(),
+ duration);
+ }
+ if (notification.actions != null) {
+ for (Notification.Action action: notification.actions) {
+ am.setPendingIntentWhitelistDuration(action.actionIntent.getTarget(), duration);
+ setPendingIntentWhitelistDuration(am, duration, action.getExtras());
+ final RemoteInput[] remoteInputs = action.getRemoteInputs();
+ if (remoteInputs != null) {
+ for (RemoteInput remoteInput : remoteInputs) {
+ setPendingIntentWhitelistDuration(am, duration, remoteInput.getExtras());
+ }
+ }
+ }
+ }
+ }
+
+ private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
+ Bundle extras) {
+ for (String key : extras.keySet()) {
+ setPendingIntentWhitelistDuration(am, duration, extras.getParcelable(key));
+ final Parcelable[] parcelableArray = extras.getParcelableArray(key);
+ if (parcelableArray != null) {
+ for (Parcelable parcelable: parcelableArray) {
+ setPendingIntentWhitelistDuration(am, duration, parcelable);
+ }
+ }
+ final ArrayList<Parcelable> parcelableList = extras.getParcelableArrayList(key);
+ if (parcelableList != null) {
+ for (Parcelable parcelable: parcelableList) {
+ setPendingIntentWhitelistDuration(am, duration, parcelable);
+ }
+ }
+ }
+ }
+
+ private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
+ Parcelable parcelable) {
+ if (parcelable instanceof PendingIntent) {
+ am.setPendingIntentWhitelistDuration(((PendingIntent) parcelable).getTarget(),
+ duration);
+ }
+ }
+
private class EnqueueNotificationRunnable implements Runnable {
private final NotificationRecord r;
private final int userId;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index ee02a99..87f0581 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -154,7 +154,6 @@
// behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
// trade-off worth doing to save boot time work.
pm.performDexOpt(pkg,
- /* instruction set */ null,
/* checkProfiles */ false,
PackageManagerService.REASON_BOOT,
/* force */ false);
@@ -192,7 +191,6 @@
// Optimize package if needed. Note that there can be no race between
// concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
if (pm.performDexOpt(pkg,
- /* instruction set */ null,
/* checkProfiles */ true,
PackageManagerService.REASON_BACKGROUND_DEXOPT,
/* force */ false)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 35bcaa9..a2bdde4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7267,7 +7267,6 @@
// behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
// trade-off worth doing to save boot time work.
int dexOptStatus = performDexOptTraced(pkg.packageName,
- null /* instructionSet */,
false /* checkProfiles */,
getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT),
false /* force */);
@@ -7309,33 +7308,33 @@
// TODO: this is not used nor needed. Delete it.
@Override
- public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
- int dexOptStatus = performDexOptTraced(packageName, instructionSet,
+ public boolean performDexOptIfNeeded(String packageName) {
+ int dexOptStatus = performDexOptTraced(packageName,
false /* checkProfiles */, getFullCompilerFilter(), false /* force */);
return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
}
@Override
- public boolean performDexOpt(String packageName, String instructionSet,
+ public boolean performDexOpt(String packageName,
boolean checkProfiles, int compileReason, boolean force) {
- int dexOptStatus = performDexOptTraced(packageName, instructionSet, checkProfiles,
+ int dexOptStatus = performDexOptTraced(packageName, checkProfiles,
getCompilerFilterForReason(compileReason), force);
return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
}
@Override
- public boolean performDexOptMode(String packageName, String instructionSet,
+ public boolean performDexOptMode(String packageName,
boolean checkProfiles, String targetCompilerFilter, boolean force) {
- int dexOptStatus = performDexOptTraced(packageName, instructionSet, checkProfiles,
+ int dexOptStatus = performDexOptTraced(packageName, checkProfiles,
targetCompilerFilter, force);
return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
}
- private int performDexOptTraced(String packageName, String instructionSet,
+ private int performDexOptTraced(String packageName,
boolean checkProfiles, String targetCompilerFilter, boolean force) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
- return performDexOptInternal(packageName, instructionSet, checkProfiles,
+ return performDexOptInternal(packageName, checkProfiles,
targetCompilerFilter, force);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -7344,10 +7343,9 @@
// Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
// if the package can now be considered up to date for the given filter.
- private int performDexOptInternal(String packageName, String instructionSet,
+ private int performDexOptInternal(String packageName,
boolean checkProfiles, String targetCompilerFilter, boolean force) {
PackageParser.Package p;
- final String targetInstructionSet;
synchronized (mPackages) {
p = mPackages.get(packageName);
if (p == null) {
@@ -7355,15 +7353,11 @@
return PackageDexOptimizer.DEX_OPT_FAILED;
}
mPackageUsage.write(false);
-
- targetInstructionSet = instructionSet != null ? instructionSet :
- getPrimaryInstructionSet(p.applicationInfo);
}
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
- final String[] instructionSets = new String[] { targetInstructionSet };
- return performDexOptInternalWithDependenciesLI(p, instructionSets, checkProfiles,
+ return performDexOptInternalWithDependenciesLI(p, checkProfiles,
targetCompilerFilter, force);
}
} finally {
@@ -7384,7 +7378,7 @@
}
private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
- String instructionSets[], boolean checkProfiles, String targetCompilerFilter,
+ boolean checkProfiles, String targetCompilerFilter,
boolean force) {
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
@@ -7396,6 +7390,7 @@
// Optimize all dependencies first. Note: we ignore the return value and march on
// on errors.
Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
+ final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
if (!deps.isEmpty()) {
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
@@ -7405,7 +7400,6 @@
getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY));
}
}
-
return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
targetCompilerFilter);
}
@@ -7477,14 +7471,11 @@
}
synchronized (mInstallLock) {
- final String[] instructionSets = new String[] {
- getPrimaryInstructionSet(pkg.applicationInfo) };
-
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
// Whoever is calling forceDexOpt wants a fully compiled package.
// Don't use profiles since that may cause compilation to be skipped.
- final int res = performDexOptInternalWithDependenciesLI(pkg, instructionSets,
+ final int res = performDexOptInternalWithDependenciesLI(pkg,
false /* checkProfiles */, getCompilerFilterForReason(REASON_FORCED_DEXOPT),
true /* force */);
@@ -16300,11 +16291,11 @@
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
removeKeystoreDataIfNeeded(userId, appId);
- final UserManager um = mContext.getSystemService(UserManager.class);
+ UserManagerInternal umInternal = getUserManagerInternal();
final int flags;
- if (um.isUserUnlockingOrUnlocked(userId)) {
+ if (umInternal.isUserUnlockingOrUnlocked(userId)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
- } else if (um.isUserRunning(userId)) {
+ } else if (umInternal.isUserRunning(userId)) {
flags = StorageManager.FLAG_STORAGE_DE;
} else {
flags = 0;
@@ -19063,11 +19054,12 @@
// Reconcile app data for all started/unlocked users
final StorageManager sm = mContext.getSystemService(StorageManager.class);
final UserManager um = mContext.getSystemService(UserManager.class);
+ UserManagerInternal umInternal = getUserManagerInternal();
for (UserInfo user : um.getUsers()) {
final int flags;
- if (um.isUserUnlockingOrUnlocked(user.id)) {
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
- } else if (um.isUserRunning(user.id)) {
+ } else if (umInternal.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
} else {
continue;
@@ -19491,11 +19483,12 @@
}
final UserManager um = mContext.getSystemService(UserManager.class);
+ UserManagerInternal umInternal = getUserManagerInternal();
for (UserInfo user : um.getUsers()) {
final int flags;
- if (um.isUserUnlockingOrUnlocked(user.id)) {
+ if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
- } else if (um.isUserRunning(user.id)) {
+ } else if (umInternal.isUserRunning(user.id)) {
flags = StorageManager.FLAG_STORAGE_DE;
} else {
continue;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index b305ba7..751c585 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -34,6 +34,8 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
@@ -66,15 +68,15 @@
}
private static void filterRecentlyUsedApps(Collection<PackageParser.Package> pkgs,
+ long estimatedPreviousSystemUseTime,
long dexOptLRUThresholdInMills) {
// Filter out packages that aren't recently used.
int total = pkgs.size();
int skipped = 0;
- long now = System.currentTimeMillis();
for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) {
PackageParser.Package pkg = i.next();
long then = pkg.getLatestForegroundPackageUseTimeInMills();
- if (then + dexOptLRUThresholdInMills < now) {
+ if (then < estimatedPreviousSystemUseTime - dexOptLRUThresholdInMills) {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Skipping dexopt of " + pkg.packageName +
" last used in foreground: " +
@@ -82,6 +84,12 @@
}
i.remove();
skipped++;
+ } else {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Will dexopt " + pkg.packageName +
+ " last used in foreground: " +
+ ((then == 0) ? "never" : new Date(then)));
+ }
}
}
if (DEBUG_DEXOPT) {
@@ -136,8 +144,24 @@
// Filter out packages that aren't recently used, add all remaining apps.
// TODO: add a property to control this?
- if (packageManagerService.isHistoricalPackageUsageAvailable()) {
- filterRecentlyUsedApps(remainingPkgs, SEVEN_DAYS_IN_MILLISECONDS);
+ if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Looking at historical package use");
+ }
+ // Get the package that was used last.
+ PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
+ Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
+ pkg2.getLatestForegroundPackageUseTimeInMills()));
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
+ }
+ long estimatedPreviousSystemUseTime =
+ lastUsed.getLatestForegroundPackageUseTimeInMills();
+ // Be defensive if for some reason package usage has bogus data.
+ if (estimatedPreviousSystemUseTime != 0) {
+ filterRecentlyUsedApps(remainingPkgs, estimatedPreviousSystemUseTime,
+ SEVEN_DAYS_IN_MILLISECONDS);
+ }
}
result.addAll(remainingPkgs);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1eeff14..beff8fc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -358,7 +358,7 @@
mInterface.clearApplicationProfileData(packageName);
}
- boolean result = mInterface.performDexOptMode(packageName, null /* instructionSet */,
+ boolean result = mInterface.performDexOptMode(packageName,
checkProfiles, targetCompilerFilter, forceCompilation);
if (!result) {
failedPackages.add(packageName);
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index db2b9f4..e667838 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -36,6 +36,8 @@
/**
* Launcher information used by {@link ShortcutService}.
+ *
+ * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
*/
class ShortcutLauncher extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
@@ -125,7 +127,8 @@
if (si == null) {
continue;
}
- if (si.isDynamic() || (prevSet != null && prevSet.contains(id))) {
+ if (si.isDynamic() || si.isManifestShortcut()
+ || (prevSet != null && prevSet.contains(id))) {
newSet.add(id);
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index c298683..bca3777 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -20,6 +20,7 @@
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
import android.os.PersistableBundle;
import android.text.format.Formatter;
@@ -28,6 +29,7 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -44,6 +46,11 @@
/**
* Package information used by {@link ShortcutService}.
+ * User information used by {@link ShortcutService}.
+ *
+ * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
+ *
+ * TODO Max dynamic shortcuts cap should be per activity.
*/
class ShortcutPackage extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
@@ -55,7 +62,6 @@
private static final String TAG_CATEGORIES = "categories";
private static final String ATTR_NAME = "name";
- private static final String ATTR_DYNAMIC_COUNT = "dynamic-count";
private static final String ATTR_CALL_COUNT = "call-count";
private static final String ATTR_LAST_RESET = "last-reset";
private static final String ATTR_ID = "id";
@@ -84,11 +90,6 @@
final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
/**
- * # of dynamic shortcuts.
- */
- private int mDynamicShortcutCount = 0;
-
- /**
* # of times the package has called rate-limited APIs.
*/
private int mApiCallCount;
@@ -129,19 +130,19 @@
* exists (as opposed to Launcher trying to fetch shortcuts from a non-existent package), so
* we do some initialization for the package.
*/
- private void onShortcutPublish() {
+ private void ensurePackageVersionInfo() {
// Make sure we have the version code for the app. We need the version code in
// handlePackageUpdated().
if (getPackageInfo().getVersionCode() < 0) {
final ShortcutService s = mShortcutUser.mService;
- final int versionCode = s.getApplicationVersionCode(getPackageName(), getOwnerUserId());
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Package %s version = %d", getPackageName(),
- versionCode));
- }
- if (versionCode >= 0) {
- getPackageInfo().setVersionCode(versionCode);
+ final PackageInfo pi = s.getPackageInfo(getPackageName(), getOwnerUserId());
+ if (pi != null) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Package %s version = %d", getPackageName(),
+ pi.versionCode));
+ }
+ getPackageInfo().updateVersionInfo(pi);
s.scheduleSaveUser(getOwnerUserId());
}
}
@@ -168,17 +169,42 @@
return mShortcuts.get(id);
}
- private ShortcutInfo deleteShortcut(@NonNull String id) {
+ private void ensureNotImmutable(@Nullable ShortcutInfo shortcut) {
+ if (shortcut != null && shortcut.isImmutable()) {
+ throw new IllegalArgumentException(
+ "Manifest shortcut ID=" + shortcut.getId()
+ + " may not be manipulated via APIs");
+ }
+ }
+
+ private void ensureNotImmutable(@NonNull String id) {
+ ensureNotImmutable(mShortcuts.get(id));
+ }
+
+ public void ensureImmutableShortcutsNotIncludedWithIds(@NonNull List<String> shortcutIds) {
+ for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+ ensureNotImmutable(shortcutIds.get(i));
+ }
+ }
+
+ public void ensureImmutableShortcutsNotIncluded(@NonNull List<ShortcutInfo> shortcuts) {
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ ensureNotImmutable(shortcuts.get(i).getId());
+ }
+ }
+
+ private ShortcutInfo deleteShortcutInner(@NonNull String id) {
final ShortcutInfo shortcut = mShortcuts.remove(id);
if (shortcut != null) {
mShortcutUser.mService.removeIcon(getPackageUserId(), shortcut);
- shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
+ shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
+ | ShortcutInfo.FLAG_MANIFEST);
}
return shortcut;
}
- void addShortcut(@NonNull ShortcutInfo newShortcut) {
- deleteShortcut(newShortcut.getId());
+ private void addShortcutInner(@NonNull ShortcutInfo newShortcut) {
+ deleteShortcutInner(newShortcut.getId());
mShortcutUser.mService.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
mShortcuts.put(newShortcut.getId(), newShortcut);
}
@@ -188,39 +214,41 @@
*
* It checks the max number of dynamic shortcuts.
*/
- public void addDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
+ public void addOrUpdateDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
- onShortcutPublish();
+ Preconditions.checkArgument(newShortcut.isEnabled(),
+ "add/setDynamicShortcuts() cannot publish disabled shortcuts");
+
+ ensurePackageVersionInfo();
newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
final boolean wasPinned;
- final int newDynamicCount;
if (oldShortcut == null) {
wasPinned = false;
- newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
} else {
+ // It's an update case.
+ // Make sure the target is updatable. (i.e. should be mutable.)
+ oldShortcut.ensureUpdatableWith(newShortcut);
+
wasPinned = oldShortcut.isPinned();
- if (oldShortcut.isDynamic()) {
- newDynamicCount = mDynamicShortcutCount; // not adding a dynamic shortcut.
- } else {
- newDynamicCount = mDynamicShortcutCount + 1; // adding a dynamic shortcut.
+ if (!oldShortcut.isEnabled()) {
+ newShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
}
}
- // Make sure there's still room.
- mShortcutUser.mService.enforceMaxDynamicShortcuts(newDynamicCount);
+ // TODO Check max dynamic count.
+ // mShortcutUser.mService.enforceMaxDynamicShortcuts(newDynamicCount);
// Okay, make it dynamic and add.
if (wasPinned) {
newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
}
- addShortcut(newShortcut);
- mDynamicShortcutCount = newDynamicCount;
+ addShortcutInner(newShortcut);
}
/**
@@ -232,7 +260,7 @@
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
- if (si.isPinned() || si.isDynamic()) continue;
+ if (si.isAlive()) continue;
if (removeList == null) {
removeList = new ArrayList<>();
@@ -241,7 +269,7 @@
}
if (removeList != null) {
for (int i = removeList.size() - 1; i >= 0; i--) {
- deleteShortcut(removeList.get(i));
+ deleteShortcutInner(removeList.get(i));
}
}
}
@@ -250,29 +278,68 @@
* Remove all dynamic shortcuts.
*/
public void deleteAllDynamicShortcuts() {
+ boolean changed = false;
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- mShortcuts.valueAt(i).clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+ if (si.isDynamic()) {
+ changed = true;
+ si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ }
}
- removeOrphans();
- mDynamicShortcutCount = 0;
+ if (changed) {
+ removeOrphans();
+ }
}
/**
* Remove a dynamic shortcut by ID.
*/
public void deleteDynamicWithId(@NonNull String shortcutId) {
+ deleteOrDisableWithId(shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false);
+ }
+
+ public void disableWithId(@NonNull String shortcutId, String disabledMessage,
+ int disabledMessageResId, boolean overrideImmutable) {
+ final ShortcutInfo disabled = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
+ overrideImmutable);
+
+ if (disabled != null) {
+ if (disabledMessage != null) {
+ disabled.setDisabledMessage(disabledMessage);
+ } else if (disabledMessageResId != 0) {
+ disabled.setDisabledMessageResId(disabledMessageResId);
+ }
+ }
+ }
+
+ @Nullable
+ private ShortcutInfo deleteOrDisableWithId(@NonNull String shortcutId, boolean disable,
+ boolean overrideImmutable) {
final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
- if (oldShortcut == null) {
- return;
+ if (oldShortcut == null || !oldShortcut.isEnabled()) {
+ return null; // Doesn't exist or already disabled.
}
- if (oldShortcut.isDynamic()) {
- mDynamicShortcutCount--;
+ if (!overrideImmutable) {
+ ensureNotImmutable(oldShortcut);
}
if (oldShortcut.isPinned()) {
- oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+ oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_MANIFEST);
+ if (disable) {
+ oldShortcut.addFlags(ShortcutInfo.FLAG_DISABLED);
+ }
+ return oldShortcut;
} else {
- deleteShortcut(shortcutId);
+ deleteShortcutInner(shortcutId);
+ return null;
+ }
+ }
+
+ public void enableWithId(@NonNull String shortcutId) {
+ final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
+ if (shortcut != null) {
+ ensureNotImmutable(shortcut);
+ shortcut.clearFlags(ShortcutInfo.FLAG_DISABLED);
}
}
@@ -430,22 +497,19 @@
for (int i = 0; i < mShortcuts.size(); i++) {
final ShortcutInfo si = mShortcuts.valueAt(i);
- // If it's called by non-launcher (i.e. publisher, always include -> true.
- // Otherwise, only include non-dynamic pinned one, if the calling launcher has pinned
- // it.
+ // Need to adjust PINNED flag depending on the caller.
+ // Basically if the caller is a launcher (callingLauncher != null) and the launcher
+ // isn't pinning it, then we need to clear PINNED for this caller.
final boolean isPinnedByCaller = (callingLauncher == null)
|| ((pinnedByCallerSet != null) && pinnedByCallerSet.contains(si.getId()));
- if (!si.isDynamic()) {
- if (!si.isPinned()) {
- s.wtf("Shortcut not pinned: package " + getPackageName()
- + ", user=" + getPackageUserId() + ", id=" + si.getId());
- continue;
- }
+
+ if (si.isFloating()) {
if (!isPinnedByCaller) {
continue;
}
}
final ShortcutInfo clone = si.clone(cloneFlag);
+
// Fix up isPinned for the caller. Note we need to do it before the "test" callback,
// since it may check isPinned.
if (!isPinnedByCaller) {
@@ -486,32 +550,81 @@
}
/**
- * Called when the package is updated. If there are shortcuts with resource icons, update
- * their timestamps.
+ * Called when the package is updated or added.
+ *
+ * Add case:
+ * - Publish manifest shortcuts.
+ *
+ * Update case:
+ * - Re-publish manifest shortcuts.
+ * - If there are shortcuts with resources (icons or strings), update their timestamps.
+ *
+ * @return TRUE if any shortcuts have been changed.
*/
- public void handlePackageUpdated(int newVersionCode) {
- if (getPackageInfo().getVersionCode() >= newVersionCode) {
- // Version hasn't changed; nothing to do.
- return;
- }
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Package %s updated, version %d -> %d", getPackageName(),
- getPackageInfo().getVersionCode(), newVersionCode));
+ public boolean handlePackageAddedOrUpdated(boolean isNewApp) {
+ final PackageInfo pi = mShortcutUser.mService.getPackageInfo(
+ getPackageName(), getPackageUserId());
+ if (pi == null) {
+ return false; // Shouldn't happen.
}
- getPackageInfo().setVersionCode(newVersionCode);
+ if (!isNewApp) {
+ // Make sure the version code or last update time has changed.
+ // Otherwise, nothing to do.
+ if (getPackageInfo().getVersionCode() >= pi.versionCode
+ && getPackageInfo().getLastUpdateTime() >= pi.lastUpdateTime) {
+ return false;
+ }
+ }
+
+ // Now prepare to publish manifest shortcuts.
+ List<ShortcutInfo> newManifestShortcutList = null;
+ try {
+ newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
+ getPackageName(), getPackageUserId());
+ } catch (IOException|XmlPullParserException e) {
+ Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
+ }
+ final int manifestShortcutSize = newManifestShortcutList == null ? 0
+ : newManifestShortcutList.size();
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Package %s has %d manifest shortcut(s)",
+ getPackageName(), manifestShortcutSize));
+ }
+ if (isNewApp && (manifestShortcutSize == 0)) {
+ // If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
+
+ // If it's an update, then it may already have manifest shortcuts, which need to be
+ // disabled.
+ return false;
+ }
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format("Package %s %s, version %d -> %d", getPackageName(),
+ (isNewApp ? "added" : "updated"),
+ getPackageInfo().getVersionCode(), pi.versionCode));
+ }
+
+ getPackageInfo().updateVersionInfo(pi);
final ShortcutService s = mShortcutUser.mService;
boolean changed = false;
- for (int i = mShortcuts.size() - 1; i >= 0; i--) {
- final ShortcutInfo si = mShortcuts.valueAt(i);
- if (si.hasAnyResources()) {
- changed = true;
- si.setTimestamp(s.injectCurrentTimeMillis());
+ // For existing shortcuts, update timestamps if they have any resources.
+ if (!isNewApp) {
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+
+ if (si.hasAnyResources()) {
+ changed = true;
+ si.setTimestamp(s.injectCurrentTimeMillis());
+ }
}
}
+
+ // (Re-)publish manifest shortcut.
+ changed |= publishManifestShortcuts(newManifestShortcutList);
+
if (changed) {
// This will send a notification to the launcher, and also save .
s.packageShortcutsChanged(getPackageName(), getPackageUserId());
@@ -519,6 +632,97 @@
// Still save the version code.
s.scheduleSaveUser(getPackageUserId());
}
+ return changed;
+ }
+
+ private boolean publishManifestShortcuts(List<ShortcutInfo> newManifestShortcutList) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format(
+ "Package %s: publishing manifest shortcuts", getPackageName()));
+ }
+ boolean changed = false;
+
+ // TODO: Check dynamic count
+
+ // TODO: Kick out dynamic if too many
+
+ // Keep the previous IDs.
+ ArraySet<String> toDisableList = null;
+ for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+ final ShortcutInfo si = mShortcuts.valueAt(i);
+
+ if (si.isManifestShortcut()) {
+ if (toDisableList == null) {
+ toDisableList = new ArraySet<>();
+ }
+ toDisableList.add(si.getId());
+ }
+ }
+
+ // Publish new ones.
+ if (newManifestShortcutList != null) {
+ final int newListSize = newManifestShortcutList.size();
+
+ for (int i = 0; i < newListSize; i++) {
+ changed = true;
+
+ final ShortcutInfo newShortcut = newManifestShortcutList.get(i);
+ final boolean newDisabled = !newShortcut.isEnabled();
+
+ final String id = newShortcut.getId();
+ final ShortcutInfo oldShortcut = mShortcuts.get(id);
+
+ boolean wasPinned = false;
+
+ if (oldShortcut != null) {
+ if (!oldShortcut.isOriginallyFromManifest()) {
+ Slog.e(TAG, "Shortcut with ID=" + newShortcut.getId()
+ + " exists but is not from AndroidManifest.xml, not updating.");
+ continue;
+ }
+ // Take over the pinned flag.
+ if (oldShortcut.isPinned()) {
+ wasPinned = true;
+ newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
+ }
+ }
+ if (newDisabled && !wasPinned) {
+ // If the shortcut is disabled, and it was *not* pinned, then this
+ // just doesn't have to be published.
+ // Just keep it in toDisableList, so the previous one would be removed.
+ continue;
+ }
+ // TODO: Check dynamic count
+
+ // Note even if enabled=false, we still need to update all fields, so do it
+ // regardless.
+ addShortcutInner(newShortcut); // This will clean up the old one too.
+
+ if (!newDisabled && toDisableList != null) {
+ // Still alive, don't remove.
+ toDisableList.remove(id);
+ }
+ }
+ }
+
+ // Disable the previous manifest shortcuts that are no longer in the manifest.
+ if (toDisableList != null) {
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, String.format(
+ "Package %s: disabling %d stale shortcuts", getPackageName(),
+ toDisableList.size()));
+ }
+ for (int i = toDisableList.size() - 1; i >= 0; i--) {
+ changed = true;
+
+ final String id = toDisableList.valueAt(i);
+
+ disableWithId(id, /* disable message =*/ null, /* disable message resid */ 0,
+ /* overrideImmutable=*/ true);
+ }
+ removeOrphans();
+ }
+ return changed;
}
public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -597,7 +801,6 @@
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
- ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
getPackageInfo().saveToXml(out);
@@ -619,7 +822,7 @@
out.startTag(null, TAG_SHORTCUT);
ShortcutService.writeAttr(out, ATTR_ID, si.getId());
// writeAttr(out, "package", si.getPackageName()); // not needed
- ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent());
+ ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivity());
// writeAttr(out, "icon", si.getIcon()); // We don't save it.
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId());
@@ -670,8 +873,6 @@
final ShortcutPackage ret = new ShortcutPackage(shortcutUser,
shortcutUser.getUserId(), packageName);
- ret.mDynamicShortcutCount =
- ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
ret.mApiCallCount =
ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
ret.mLastResetTime =
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index ae9709e..7f5d931 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.pm.PackageInfo;
import android.util.Slog;
@@ -34,12 +35,15 @@
/**
* Package information used by {@link android.content.pm.ShortcutManager} for backup / restore.
+ *
+ * All methods should be guarded by {@code ShortcutService.mLock}.
*/
class ShortcutPackageInfo {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package-info";
private static final String ATTR_VERSION = "version";
+ private static final String ATTR_LAST_UPDATE_TIME = "last_udpate_time";
private static final String ATTR_SHADOW = "shadow";
private static final String TAG_SIGNATURE = "signature";
@@ -53,16 +57,20 @@
*/
private boolean mIsShadow;
private int mVersionCode = VERSION_UNKNOWN;
+ private long mLastUpdateTime;
private ArrayList<byte[]> mSigHashes;
- private ShortcutPackageInfo(int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
+ private ShortcutPackageInfo(int versionCode, long lastUpdateTime,
+ ArrayList<byte[]> sigHashes, boolean isShadow) {
mVersionCode = versionCode;
+ mLastUpdateTime = lastUpdateTime;
mIsShadow = isShadow;
mSigHashes = sigHashes;
}
public static ShortcutPackageInfo newEmpty() {
- return new ShortcutPackageInfo(VERSION_UNKNOWN, new ArrayList<>(0), /* isShadow */ false);
+ return new ShortcutPackageInfo(VERSION_UNKNOWN, /* last update time =*/ 0,
+ new ArrayList<>(0), /* isShadow */ false);
}
public boolean isShadow() {
@@ -77,8 +85,15 @@
return mVersionCode;
}
- public void setVersionCode(int versionCode) {
- mVersionCode = versionCode;
+ public long getLastUpdateTime() {
+ return mLastUpdateTime;
+ }
+
+ public void updateVersionInfo(@NonNull PackageInfo pi) {
+ if (pi != null) {
+ mVersionCode = pi.versionCode;
+ mLastUpdateTime = pi.lastUpdateTime;
+ }
}
public boolean hasSignatures() {
@@ -111,7 +126,7 @@
Slog.e(TAG, "Can't get signatures: package=" + packageName);
return null;
}
- final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode,
+ final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime,
BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
return ret;
@@ -131,6 +146,7 @@
return;
}
mVersionCode = pi.versionCode;
+ mLastUpdateTime = pi.lastUpdateTime;
mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
}
@@ -139,6 +155,7 @@
out.startTag(null, TAG_ROOT);
ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
+ ShortcutService.writeAttr(out, ATTR_LAST_UPDATE_TIME, mLastUpdateTime);
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
for (int i = 0; i < mSigHashes.size(); i++) {
@@ -154,6 +171,9 @@
final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
+ final long lastUpdateTime = ShortcutService.parseIntAttribute(
+ parser, ATTR_LAST_UPDATE_TIME);
+
// When restoring from backup, it's always shadow.
final boolean shadow =
fromBackup || ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
@@ -185,6 +205,7 @@
// Successfully loaded; replace the feilds.
mVersionCode = versionCode;
+ mLastUpdateTime = lastUpdateTime;
mIsShadow = shadow;
mSigHashes = hashes;
}
@@ -205,6 +226,11 @@
pw.print(mVersionCode);
pw.println();
+ pw.print(prefix);
+ pw.print(" Last package update time: ");
+ pw.print(mLastUpdateTime);
+ pw.println();
+
for (int i = 0; i < mSigHashes.size(); i++) {
pw.print(prefix);
pw.print(" ");
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 0c2417c..d08b974 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -26,6 +26,9 @@
import java.io.IOException;
+/**
+ * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
+ */
abstract class ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
new file mode 100644
index 0000000..44d0a9f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2016 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.server.pm;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.ShortcutInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class ShortcutParser {
+ private static final String TAG = ShortcutService.TAG;
+
+ private static final boolean DEBUG = ShortcutService.DEBUG || false; // DO NOT SUBMIT WITH TRUE
+
+ @VisibleForTesting
+ static final String METADATA_KEY = "android.pm.Shortcuts";
+
+ private static final String TAG_SHORTCUTS = "shortcuts";
+ private static final String TAG_SHORTCUT = "shortcut";
+
+ @Nullable
+ public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
+ String packageName, @UserIdInt int userId) throws IOException, XmlPullParserException {
+ final PackageInfo pi = service.injectGetActivitiesWithMetadata(packageName, userId);
+
+ List<ShortcutInfo> result = null;
+
+ if (pi != null && pi.activities != null) {
+ for (ActivityInfo activityInfo : pi.activities) {
+ result = parseShortcutsOneFile(service, activityInfo, packageName, userId, result);
+ }
+ }
+ return result;
+ }
+
+ private static List<ShortcutInfo> parseShortcutsOneFile(
+ ShortcutService service,
+ ActivityInfo activityInfo, String packageName, @UserIdInt int userId,
+ List<ShortcutInfo> result) throws IOException, XmlPullParserException {
+ XmlResourceParser parser = null;
+ try {
+ parser = service.injectXmlMetaData(activityInfo, METADATA_KEY);
+ if (parser == null) {
+ return result;
+ }
+
+ final ComponentName activity = new ComponentName(packageName, activityInfo.name);
+
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+
+ outer:
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
+
+ if (depth == 1 && TAG_SHORTCUTS.equals(tag)) {
+ continue; // Root tag.
+ }
+ if (depth == 2 && TAG_SHORTCUT.equals(tag)) {
+ final ShortcutInfo si = parseShortcutAttributes(
+ service, attrs, packageName, activity, userId);
+ if (ShortcutService.DEBUG) {
+ Slog.d(TAG, "Shortcut=" + si);
+ }
+ if (result != null) {
+ for (int i = result.size() - 1; i >= 0; i--) {
+ if (si.getId().equals(result.get(i).getId())) {
+ Slog.w(TAG, "Duplicate shortcut ID detected, skipping.");
+ continue outer;
+ }
+ }
+ }
+
+ if (si != null) {
+ if (result == null) {
+ result = new ArrayList<>();
+ }
+ result.add(si);
+ }
+ continue;
+ }
+ Slog.w(TAG, "Unknown tag " + tag);
+ }
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ return result;
+ }
+
+ private static ShortcutInfo parseShortcutAttributes(ShortcutService service,
+ AttributeSet attrs, String packageName, ComponentName activity,
+ @UserIdInt int userId) {
+ final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+ R.styleable.Shortcut);
+ try {
+ final String id = sa.getString(R.styleable.Shortcut_shortcutId);
+ final boolean enabled = sa.getBoolean(R.styleable.Shortcut_enabled, true);
+ final int rank = sa.getInt(R.styleable.Shortcut_shortcutRank, 0);
+ final int iconResId = sa.getResourceId(R.styleable.Shortcut_shortcutIcon, 0);
+ final int titleResId = sa.getResourceId(R.styleable.Shortcut_shortcutTitle, 0);
+ final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutText, 0);
+ final int disabledMessageResId = sa.getResourceId(
+ R.styleable.Shortcut_shortcutDisabledMessage, 0);
+ final String categories = sa.getString(R.styleable.Shortcut_shortcutCategories);
+ String intentAction = sa.getString(R.styleable.Shortcut_shortcutIntentAction);
+ final String intentData = sa.getString(R.styleable.Shortcut_shortcutIntentData);
+
+ if (TextUtils.isEmpty(id)) {
+ Slog.w(TAG, "Shortcut ID must be provided. activity=" + activity);
+ return null;
+ }
+ if (titleResId == 0) {
+ Slog.w(TAG, "Shortcut title must be provided. activity=" + activity);
+ return null;
+ }
+ if (TextUtils.isEmpty(intentAction)) {
+ if (enabled) {
+ Slog.w(TAG, "Shortcut intent action must be provided. activity=" + activity);
+ return null;
+ } else {
+ // Disabled shortcut doesn't have to have an action, but just set VIEW as the
+ // default.
+ intentAction = Intent.ACTION_VIEW;
+ }
+ }
+
+ final ArraySet<String> categoriesSet;
+ if (categories == null) {
+ categoriesSet = null;
+ } else {
+ final String[] arr = categories.split(":");
+ categoriesSet = new ArraySet<>(arr.length);
+ for (String v : arr) {
+ categoriesSet.add(v);
+ }
+ }
+ final Intent intent = new Intent(intentAction);
+ if (!TextUtils.isEmpty(intentData)) {
+ intent.setData(Uri.parse(intentData));
+ }
+
+ return createShortcutFromManifest(
+ service,
+ userId,
+ id,
+ packageName,
+ activity,
+ titleResId,
+ textResId,
+ disabledMessageResId,
+ categoriesSet,
+ intent,
+ rank,
+ iconResId,
+ enabled);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static ShortcutInfo createShortcutFromManifest(ShortcutService service,
+ @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
+ int titleResId, int textResId, int disabledMessageResId, Set<String> categories,
+ Intent intent, int rank, int iconResId, boolean enabled) {
+
+ final int flags =
+ (enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
+ | ShortcutInfo.FLAG_IMMUTABLE
+ | ((iconResId != 0) ? ShortcutInfo.FLAG_HAS_ICON_RES : 0);
+
+ return new ShortcutInfo(
+ userId,
+ id,
+ packageName,
+ activityComponent,
+ null, // icon
+ null, // title string
+ titleResId,
+ null, // text string
+ textResId,
+ null, // disabled message string
+ disabledMessageResId,
+ categories,
+ intent,
+ null, // intent extras
+ rank,
+ null, // extras
+ service.injectCurrentTimeMillis(),
+ flags,
+ iconResId,
+ null); // bitmap path
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 6a10f41..dc0404c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -25,6 +25,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutService;
@@ -39,6 +40,7 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
+import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Canvas;
@@ -103,6 +105,7 @@
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -111,16 +114,21 @@
/**
* TODO:
- * - Manifest shortcuts.
+ * - Implement # of dynamic shortcuts cap.
*
- * - Implement disableShortcuts().
+ * - HandleUnlockUser needs to be async. Wait on it in onCleanupUser.
*
* - Implement reportShortcutUsed().
*
+ * - validateForXml() should be removed.
+ *
* - Ranks should be recalculated after each update.
*
* - When the system locale changes, update timestamps for shortcuts with string resources,
- * and notify the launcher.
+ * and notify the launcher. Right now, it resets the throttling, but timestamps are not changed
+ * and there's no notification either.
+ *
+ * - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
*
* - Default launcher check does take a few ms. Worth caching.
*
@@ -128,11 +136,13 @@
* internal bitmap handling.
*
* - Add more call stats.
+ *
+ * - Rename getMaxDynamicShortcutCount and mMaxDynamicShortcuts
*/
public class ShortcutService extends IShortcutService.Stub {
static final String TAG = "ShortcutService";
- static final boolean DEBUG = false; // STOPSHIP if true
+ static final boolean DEBUG = true; // STOPSHIP if true
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
@@ -301,8 +311,11 @@
int GET_APPLICATION_INFO = 3;
int LAUNCHER_PERMISSION_CHECK = 4;
int CLEANUP_DANGLING_BITMAPS = 5;
+ int GET_ACTIVITIES_WITH_METADATA = 6;
+ int GET_INSTALLED_APPLICATIONS = 7;
+ int CHECK_PACKAGE_CHANGES = 8;
- int COUNT = CLEANUP_DANGLING_BITMAPS + 1;
+ int COUNT = CHECK_PACKAGE_CHANGES + 1;
}
final Object mStatLock = new Object();
@@ -444,6 +457,9 @@
/** lifecycle event */
void handleUnlockUser(int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "handleUnlockUser: user=" + userId);
+ }
synchronized (mLock) {
// Preload
getUserShortcutsLocked(userId);
@@ -642,10 +658,10 @@
out.endTag(null, tag);
}
- static void writeAttr(XmlSerializer out, String name, String value) throws IOException {
+ static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException {
if (TextUtils.isEmpty(value)) return;
- out.attribute(null, name, value);
+ out.attribute(null, name, value.toString());
}
static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
@@ -1090,7 +1106,7 @@
FileOutputStreamWithPath openIconFileForWrite(@UserIdInt int userId, ShortcutInfo shortcut)
throws IOException {
final File packagePath = new File(getUserBitmapFilePath(userId),
- shortcut.getPackageName());
+ shortcut.getPackage());
if (!packagePath.isDirectory()) {
packagePath.mkdirs();
if (!packagePath.isDirectory()) {
@@ -1129,7 +1145,6 @@
}
Bitmap bitmap;
- Bitmap bitmapToRecycle = null;
try {
switch (icon.getType()) {
case Icon.TYPE_RESOURCE: {
@@ -1181,9 +1196,6 @@
}
}
} finally {
- if (bitmapToRecycle != null) {
- bitmapToRecycle.recycle();
- }
// Once saved, we won't use the original icon information, so null it out.
shortcut.clearIcon();
}
@@ -1196,7 +1208,7 @@
// so override in unit tests.
// TODO CTS this case.
void injectValidateIconResPackage(ShortcutInfo shortcut, Icon icon) {
- if (!shortcut.getPackageName().equals(icon.getResPackage())) {
+ if (!shortcut.getPackage().equals(icon.getResPackage())) {
throw new IllegalArgumentException(
"Icon resource must reside in shortcut owner package");
}
@@ -1353,10 +1365,9 @@
*/
private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
Preconditions.checkNotNull(shortcut, "Null shortcut detected");
- if (shortcut.getActivityComponent() != null) {
+ if (shortcut.getActivity() != null) {
Preconditions.checkState(
- shortcut.getPackageName().equals(
- shortcut.getActivityComponent().getPackageName()),
+ shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
"Activity package name mismatch");
}
@@ -1399,7 +1410,7 @@
}
}
- private static void validateForXml(String s) {
+ private static void validateForXml(CharSequence s) {
if (TextUtils.isEmpty(s)) {
return;
}
@@ -1427,6 +1438,8 @@
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+
// Throttling.
if (!ps.tryApiCall()) {
return false;
@@ -1444,7 +1457,7 @@
// Then, add/update all. We need to make sure to take over "pinned" flag.
for (int i = 0; i < size; i++) {
final ShortcutInfo newShortcut = newShortcuts.get(i);
- ps.addDynamicShortcut(newShortcut);
+ ps.addOrUpdateDynamicShortcut(newShortcut);
}
}
packageShortcutsChanged(packageName, userId);
@@ -1462,6 +1475,8 @@
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+
// Throttling.
if (!ps.tryApiCall()) {
return false;
@@ -1473,11 +1488,22 @@
final ShortcutInfo target = ps.findShortcutById(source.getId());
if (target != null) {
+ if (target.isEnabled() != source.isEnabled()) {
+ Slog.w(TAG,
+ "ShortcutInfo.enabled cannot be changed with updateShortcuts()");
+ }
+
final boolean replacingIcon = (source.getIcon() != null);
if (replacingIcon) {
removeIcon(userId, target);
}
+ if (source.getActivity() != null &&
+ !source.getActivity().equals(target.getActivity())) {
+ // TODO When activity is changing, check the dynamic count.
+ }
+
+ // Note copyNonNullFieldsFrom() does the "udpatable with?" check too.
target.copyNonNullFieldsFrom(source);
if (replacingIcon) {
@@ -1502,6 +1528,8 @@
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.ensureImmutableShortcutsNotIncluded(newShortcuts);
+
// Throttling.
if (!ps.tryApiCall()) {
return false;
@@ -1513,7 +1541,7 @@
fixUpIncomingShortcutInfo(newShortcut, /* forUpdate= */ false);
// Add it.
- ps.addDynamicShortcut(newShortcut);
+ ps.addOrUpdateDynamicShortcut(newShortcut);
}
}
packageShortcutsChanged(packageName, userId);
@@ -1528,7 +1556,32 @@
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
synchronized (mLock) {
- // TODO implement it.
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+
+ for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+ ps.disableWithId(Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)),
+ disabledMessage, disabledMessageResId,
+ /* overrideImmutable=*/ false);
+ }
+ }
+ packageShortcutsChanged(packageName, userId);
+ }
+
+ @Override
+ public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+
+ synchronized (mLock) {
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+
+ for (int i = shortcutIds.size() - 1; i >= 0; i--) {
+ ps.enableWithId((String) shortcutIds.get(i));
+ }
}
packageShortcutsChanged(packageName, userId);
}
@@ -1540,8 +1593,12 @@
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
synchronized (mLock) {
+ final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+
+ ps.ensureImmutableShortcutsNotIncludedWithIds((List<String>) shortcutIds);
+
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
- getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(
+ ps.deleteDynamicWithId(
Preconditions.checkStringNotEmpty((String) shortcutIds.get(i)));
}
}
@@ -1570,6 +1627,17 @@
}
@Override
+ public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
+ @UserIdInt int userId) {
+ verifyCaller(packageName, userId);
+ synchronized (mLock) {
+ return getShortcutsWithQueryLocked(
+ packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
+ ShortcutInfo::isManifestShortcut);
+ }
+ }
+
+ @Override
public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
@@ -1883,18 +1951,24 @@
return false;
}
if (componentName != null) {
- if (si.getActivityComponent() != null
- && !si.getActivityComponent().equals(componentName)) {
+ if (si.getActivity() != null
+ && !si.getActivity().equals(componentName)) {
return false;
}
}
- final boolean matchDynamic =
- ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
- && si.isDynamic();
- final boolean matchPinned =
- ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
- && si.isPinned();
- return matchDynamic || matchPinned;
+ if (((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
+ && si.isDynamic()) {
+ return true;
+ }
+ if (((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
+ && si.isPinned()) {
+ return true;
+ }
+ if (((queryFlags & ShortcutQuery.FLAG_GET_MANIFEST) != 0)
+ && si.isManifestShortcut()) {
+ return true;
+ }
+ return false;
}, cloneFlag, callingPackage, launcherUserId);
}
@@ -1967,7 +2041,7 @@
final ShortcutInfo si = getShortcutInfoLocked(
launcherUserId, callingPackage, packageName, shortcutId, userId);
// "si == null" should suffice here, but check the flags too just to make sure.
- if (si == null || !(si.isDynamic() || si.isPinned())) {
+ if (si == null || !si.isEnabled() || !si.isAlive()) {
return null;
}
return si.getIntent();
@@ -2114,31 +2188,37 @@
if (DEBUG) {
Slog.d(TAG, "checkPackageChanges() ownerUserId=" + ownerUserId);
}
- final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
- synchronized (mLock) {
- final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
+ final long start = injectElapsedRealtime();
+ try {
+ final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
- user.forAllPackageItems(spi -> {
- if (spi.getPackageInfo().isShadow()) {
- return; // Don't delete shadow information.
+ synchronized (mLock) {
+ final ShortcutUser user = getUserShortcutsLocked(ownerUserId);
+
+ // Find packages that have been uninstalled.
+ user.forAllPackageItems(spi -> {
+ if (spi.getPackageInfo().isShadow()) {
+ return; // Don't delete shadow information.
+ }
+ if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
+ gonePackages.add(PackageWithUser.of(spi));
+ }
+ });
+ if (gonePackages.size() > 0) {
+ for (int i = gonePackages.size() - 1; i >= 0; i--) {
+ final PackageWithUser pu = gonePackages.get(i);
+ cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId);
+ }
}
- final int versionCode = getApplicationVersionCode(
- spi.getPackageName(), spi.getPackageUserId());
- if (versionCode >= 0) {
- // Package still installed, see if it's updated.
- getUserShortcutsLocked(ownerUserId).handlePackageUpdated(
- spi.getPackageName(), versionCode);
- } else {
- gonePackages.add(PackageWithUser.of(spi));
- }
- });
- if (gonePackages.size() > 0) {
- for (int i = gonePackages.size() - 1; i >= 0; i--) {
- final PackageWithUser pu = gonePackages.get(i);
- cleanUpPackageLocked(pu.packageName, ownerUserId, pu.userId);
- }
+
+ // Then for each installed app, publish manifest shortcuts when needed.
+ forInstalledApplications(ownerUserId, ai -> {
+ user.handlePackageAddedOrUpdated(ai.packageName);
+ });
}
+ } finally {
+ logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start);
}
}
@@ -2147,8 +2227,9 @@
Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
}
synchronized (mLock) {
- forEachLoadedUserLocked(user ->
- user.attemptToRestoreIfNeededAndSave(this, packageName, userId));
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
+ user.handlePackageAddedOrUpdated(packageName);
}
}
@@ -2158,14 +2239,12 @@
packageName, userId));
}
synchronized (mLock) {
- forEachLoadedUserLocked(user ->
- user.attemptToRestoreIfNeededAndSave(this, packageName, userId));
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
- final int versionCode = getApplicationVersionCode(packageName, userId);
- if (versionCode < 0) {
- return; // shouldn't happen
+ if (isPackageInstalled(packageName, userId)) {
+ user.handlePackageAddedOrUpdated(packageName);
}
- getUserShortcutsLocked(userId).handlePackageUpdated(packageName, versionCode);
}
}
@@ -2187,10 +2266,16 @@
// === PackageManager interaction ===
+ @Nullable
PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
return injectPackageInfo(packageName, userId, true);
}
+ @Nullable
+ PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) {
+ return injectPackageInfo(packageName, userId, false);
+ }
+
int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
final long token = injectClearCallingIdentity();
try {
@@ -2205,6 +2290,7 @@
}
}
+ @Nullable
@VisibleForTesting
PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
boolean getSignatures) {
@@ -2227,6 +2313,7 @@
}
}
+ @Nullable
@VisibleForTesting
ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
final long start = injectElapsedRealtime();
@@ -2244,6 +2331,61 @@
}
}
+ @Nullable
+ @VisibleForTesting
+ PackageInfo injectGetActivitiesWithMetadata(String packageName, @UserIdInt int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ return mIPackageManager.getPackageInfo(packageName,
+ PACKAGE_MATCH_FLAGS | PackageManager.GET_ACTIVITIES
+ | PackageManager.GET_META_DATA, userId);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, "RemoteException", e);
+ return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.GET_ACTIVITIES_WITH_METADATA, start);
+ }
+ }
+
+ @Nullable
+ @VisibleForTesting
+ List<ApplicationInfo> injectInstalledApplications(@UserIdInt int userId) {
+ final long start = injectElapsedRealtime();
+ final long token = injectClearCallingIdentity();
+ try {
+ final ParceledListSlice<ApplicationInfo> parceledList =
+ mIPackageManager.getInstalledApplications(PACKAGE_MATCH_FLAGS, userId);
+ if (parceledList == null) {
+ return Collections.emptyList();
+ }
+ return parceledList.getList();
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Slog.wtf(TAG, "RemoteException", e);
+ return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
+
+ logDurationStat(Stats.GET_INSTALLED_APPLICATIONS, start);
+ }
+ }
+
+ private void forInstalledApplications(@UserIdInt int userId,
+ Consumer<ApplicationInfo> callback) {
+ final List<ApplicationInfo> list = injectInstalledApplications(userId);
+ for (int i = list.size() - 1; i >= 0; i--) {
+ final ApplicationInfo ai = list.get(i);
+
+ if ((ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+ callback.accept(ai);
+ }
+ }
+ }
+
private boolean isApplicationFlagSet(String packageName, int userId, int flags) {
final ApplicationInfo ai = injectApplicationInfo(packageName, userId);
return (ai != null) && ((ai.flags & flags) == flags);
@@ -2253,15 +2395,10 @@
return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
}
- /**
- * @return the version code of the package, or -1 if the app is not installed.
- */
- int getApplicationVersionCode(String packageName, int userId) {
- final ApplicationInfo ai = injectApplicationInfo(packageName, userId);
- if ((ai == null) || ((ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
- return -1;
- }
- return ai.versionCode;
+ @Nullable
+ XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
+// TODO Doesn't seem like ACROSS_USER is needed, but double check.
+ return activityInfo.loadXmlMetaData(mContext.getPackageManager(), key);
}
// === Backup & restore ===
@@ -2402,8 +2539,10 @@
dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()");
dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)");
dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
-
dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps");
+ dumpStatLS(pw, p, Stats.GET_ACTIVITIES_WITH_METADATA, "getActivities+metadata");
+ dumpStatLS(pw, p, Stats.GET_INSTALLED_APPLICATIONS, "getInstalledApplications");
+ dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges");
}
for (int i = 0; i < mUsers.size(); i++) {
@@ -2783,12 +2922,19 @@
}
@VisibleForTesting
- ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
+ ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
synchronized (mLock) {
final ShortcutUser user = mUsers.get(userId);
if (user == null) return null;
- final ShortcutPackage pkg = user.getAllPackagesForTest().get(packageName);
+ return user.getAllPackagesForTest().get(packageName);
+ }
+ }
+
+ @VisibleForTesting
+ ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
+ synchronized (mLock) {
+ final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
if (pkg == null) return null;
return pkg.findShortcutById(shortcutId);
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index d38cfba..840c3df 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -24,6 +24,7 @@
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
@@ -40,6 +41,8 @@
/**
* User information used by {@link ShortcutService}.
+ *
+ * All methods should be guarded by {@code #mService.mLock}.
*/
class ShortcutUser {
private static final String TAG = ShortcutService.TAG;
@@ -230,19 +233,16 @@
}
}
- /**
- * Called when a package is updated.
- */
- public void handlePackageUpdated(@NonNull String packageName,
- int newVersionCode) {
- if (!mPackages.containsKey(packageName)) {
- return;
+ public void handlePackageAddedOrUpdated(@NonNull String packageName) {
+ final boolean isNewApp = !mPackages.containsKey(packageName);
+
+ final ShortcutPackage shortcutPackage = getPackageShortcuts(packageName);
+
+ if (!shortcutPackage.handlePackageAddedOrUpdated(isNewApp)) {
+ if (isNewApp) {
+ mPackages.remove(packageName);
+ }
}
- final ShortcutPackage p = getPackageShortcutsIfExists(packageName);
- if (p == null) {
- return; // No need to instantiate ShortcutPackage.
- }
- p.handlePackageUpdated(newVersionCode);
}
public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4541e2e..9c62b20 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,7 +22,6 @@
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManager;
@@ -32,7 +31,6 @@
import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.KeyguardManager;
-import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -78,6 +76,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.Xml;
@@ -90,6 +89,7 @@
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
+import com.android.server.am.UserState;
import libcore.io.IoUtils;
import libcore.util.Objects;
@@ -331,8 +331,8 @@
@GuardedBy("mUsersLock")
private boolean mForceEphemeralUsers;
- @GuardedBy("mUsersLock")
- private final SparseBooleanArray mUnlockingOrUnlockedUsers = new SparseBooleanArray();
+ @GuardedBy("mUserStates")
+ private final SparseIntArray mUserStates = new SparseIntArray();
private static UserManagerService sInstance;
@@ -379,6 +379,7 @@
mLocalService = new LocalService();
LocalServices.addService(UserManagerInternal.class, mLocalService);
mLockPatternUtils = new LockPatternUtils(mContext);
+ mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING);
}
void systemReady() {
@@ -2380,7 +2381,9 @@
synchronized (mUsersLock) {
mUsers.remove(userHandle);
mIsUserManaged.delete(userHandle);
- mUnlockingOrUnlockedUsers.delete(userHandle);
+ }
+ synchronized (mUserStates) {
+ mUserStates.delete(userHandle);
}
synchronized (mRestrictionsLock) {
mBaseUserRestrictions.remove(userHandle);
@@ -3081,6 +3084,9 @@
pw.println();
pw.println(" Device managed: " + mIsDeviceManaged);
}
+ synchronized (mUserStates) {
+ pw.println(" Started users state: " + mUserStates);
+ }
// Dump some capabilities
pw.println();
pw.println(" Max users: " + UserManager.getMaxSupportedUsers());
@@ -3267,16 +3273,32 @@
}
@Override
- public void setUserUnlockingOrUnlocked(int userId, boolean unlockingOrUnlocked) {
- synchronized (mUsersLock) {
- mUnlockingOrUnlockedUsers.put(userId, unlockingOrUnlocked);
+ public boolean isUserRunning(int userId) {
+ synchronized (mUserStates) {
+ return mUserStates.get(userId, -1) >= 0;
+ }
+ }
+
+ @Override
+ public void setUserState(int userId, int userState) {
+ synchronized (mUserStates) {
+ mUserStates.put(userId, userState);
+ }
+ }
+
+ @Override
+ public void removeUserState(int userId) {
+ synchronized (mUserStates) {
+ mUserStates.delete(userId);
}
}
@Override
public boolean isUserUnlockingOrUnlocked(int userId) {
- synchronized (mUsersLock) {
- return mUnlockingOrUnlockedUsers.get(userId);
+ synchronized (mUserStates) {
+ int state = mUserStates.get(userId, -1);
+ return (state == UserState.STATE_RUNNING_UNLOCKING)
+ || (state == UserState.STATE_RUNNING_UNLOCKED);
}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f0e35e0..14ed190 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4360,6 +4360,11 @@
pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
// ...with content insets above the nav bar
cf.bottom = vf.bottom = mStableBottom;
+ if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
+ // The status bar forces the navigation bar while it's visible. Make sure the IME
+ // avoids the navigation bar in that case.
+ pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+ }
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
mDockLayer = win.getSurfaceLayer();
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 1b31d07c..e73649d 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -45,9 +45,11 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
+import com.android.server.LocalServices;
import com.android.server.wm.DimLayer.DimLayerUser;
import com.android.server.wm.WindowManagerService.H;
@@ -131,6 +133,7 @@
private float mLastAnimationProgress;
private float mLastDividerProgress;
private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
+ private boolean mImeHideRequested;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -375,11 +378,39 @@
}
}
mDockedStackListeners.finishBroadcast();
- if (!exists) {
+ if (exists) {
+ InputMethodManagerInternal inputMethodManagerInternal =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ if (inputMethodManagerInternal != null) {
+
+ // Hide the current IME to avoid problems with animations from IME adjustment when
+ // attaching the docked stack.
+ inputMethodManagerInternal.hideCurrentInputMethod();
+ mImeHideRequested = true;
+ }
+ } else {
setMinimizedDockedStack(false);
}
}
+ /**
+ * Resets the state that IME hide has been requested. See {@link #isImeHideRequested}.
+ */
+ void resetImeHideRequested() {
+ mImeHideRequested = false;
+ }
+
+ /**
+ * The docked stack divider controller makes sure the IME gets hidden when attaching the docked
+ * stack, to avoid animation problems. This flag indicates whether the request to hide the IME
+ * has been sent in an asynchronous manner, and the IME should be treated as hidden already.
+ *
+ * @return whether IME hide request has been sent
+ */
+ boolean isImeHideRequested() {
+ return mImeHideRequested;
+ }
+
void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
@@ -606,7 +637,12 @@
private void setMinimizedDockedStack(boolean minimized) {
final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
notifyDockedStackMinimizedChanged(minimized, 0);
- setMinimizeAmount(stack, minimized ? 1f : 0f);
+ if (stack == null) {
+ return;
+ }
+ if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) {
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
}
private boolean isAnimationMaximizing() {
@@ -690,8 +726,11 @@
float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
.getInterpolation(t);
- setMinimizeAmount(stack, getMinimizeAmount(stack, t));
-
+ if (stack != null) {
+ if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
if (t >= 1.0f) {
mAnimatingForMinimizedDockedStack = false;
return false;
@@ -700,42 +739,6 @@
}
}
- void setMinimizeAmount(TaskStack dockedStack, float minimizeAmount) {
- final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
-
- // If the docked stack is not visible, clear the complementary stack on all stacks.
- if (dockedStack == null) {
- for (int i = stacks.size() - 1; i >= 0; --i) {
- final TaskStack stack = stacks.get(i);
- stack.resetAdjustedForComplementDock();
- }
- return;
- }
-
- // Otherwise if the docked stack minimize amount has changed, update the adjusted bounds
- // on the other stack that's currently visible, so that the stack's getDimBounds()
- // occupies what's left by the docked stack. This is needed so that stuff like wallpaper
- // gets cropped properly to the area left by the dock.
- if (dockedStack.setAdjustedForMinimizedDock(minimizeAmount)) {
- final boolean adjusted =
- dockedStack.isVisibleForUserLocked() && minimizeAmount != 0.0f;
- dockedStack.getDimBounds(mTmpRect2);
- int dockSide = dockedStack.getDockSide();
- for (int i = stacks.size() - 1; i >= 0; --i) {
- final TaskStack stack = stacks.get(i);
- if (stack == dockedStack) {
- continue;
- }
- if (stack.isVisibleLocked() && adjusted) {
- stack.setAdjustedForComplementDock(mTmpRect2, dockSide);
- } else {
- stack.resetAdjustedForComplementDock();
- }
- }
- mService.mWindowPlacerLocked.performSurfacePlacement();
- }
- }
-
private float getInterpolatedAnimationValue(float t) {
return t * mAnimationTarget + (1 - t) * mAnimationStart;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 114d9be..8be5b19 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -123,7 +123,6 @@
private float mAdjustImeAmount;
private float mAdjustDividerAmount;
private final int mDockedStackMinimizeThickness;
- private boolean mAdjustedForForComplementDock;
// If this is true, we are in the bounds animating mode.
// The task will be down or upscaled to perfectly fit the
@@ -243,9 +242,7 @@
insetBounds = mFullyAdjustedImeBounds;
}
}
- if (!mAdjustedForForComplementDock) {
- alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
- }
+ alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
mDisplayContent.layoutNeeded = true;
}
@@ -864,7 +861,6 @@
mImeWin = imeWin;
mImeGoingAway = false;
if (!mAdjustedForIme || forceUpdate) {
- mAdjustedForForComplementDock = false;
mAdjustedForIme = true;
mAdjustImeAmount = 0f;
mAdjustDividerAmount = 0f;
@@ -927,12 +923,10 @@
* @return Whether the amount has changed and a layout is needed.
*/
boolean setAdjustedForMinimizedDock(float minimizeAmount) {
- mAdjustedForForComplementDock = false;
-
if (minimizeAmount != mMinimizeAmount) {
mMinimizeAmount = minimizeAmount;
updateAdjustedBounds();
- return true;
+ return isVisibleForUserLocked();
} else {
return false;
}
@@ -942,33 +936,6 @@
return mMinimizeAmount != 0f;
}
- void setAdjustedForComplementDock(Rect dockBounds, int dockSide) {
- if (mMinimizeAmount != 0f || mAdjustedForIme) {
- return;
- }
- mTmpAdjustedBounds.set(mBounds);
- if (dockSide == DOCKED_TOP) {
- mTmpAdjustedBounds.top = dockBounds.bottom;
- } else if (dockSide == DOCKED_LEFT) {
- mTmpAdjustedBounds.left = dockBounds.right;
- } else if (dockSide == DOCKED_RIGHT) {
- mTmpAdjustedBounds.right = dockBounds.left;
- } else {
- Slog.w(TAG_WM, "setAdjustedForComplementDock: invalid dock side " + dockSide);
- return;
- }
- mAdjustedForForComplementDock = true;
- setAdjustedBounds(mTmpAdjustedBounds);
- }
-
- void resetAdjustedForComplementDock() {
- if (mAdjustedForForComplementDock) {
- mAdjustedForForComplementDock = false;
- mTmpAdjustedBounds.setEmpty();
- setAdjustedBounds(mTmpAdjustedBounds);
- }
- }
-
/**
* Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
* to the list of to be drawn windows the service is waiting for.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 695ed36b..3f4c1d5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3569,6 +3569,8 @@
final ArrayList<Task> tasks = displayContent.getTasks();
final boolean inMultiWindow = isStackVisibleLocked(DOCKED_STACK_ID)
|| isStackVisibleLocked(FREEFORM_WORKSPACE_STACK_ID);
+ final boolean dockMinimized =
+ getDefaultDisplayContentLocked().mDividerControllerLocked.isMinimizedDock();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
final int firstToken = tokens.size() - 1;
@@ -3603,8 +3605,10 @@
continue;
}
- // No app except the home app may specify the screen orientation in multi-window.
- if (inMultiWindow && !atoken.mTask.isHomeTask()) {
+ // No app except the home app may specify the screen orientation in multi-window,
+ // and only if the docked stack is minimized to avoid weirdness when home task
+ // temporarily gets moved to the front.
+ if (inMultiWindow && (!atoken.mTask.isHomeTask() || !dockMinimized)) {
continue;
}
@@ -7596,7 +7600,8 @@
void adjustForImeIfNeeded(final DisplayContent displayContent) {
final WindowState imeWin = mInputMethodWindow;
- final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw();
+ final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
+ && !displayContent.mDividerControllerLocked.isImeHideRequested();
final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID);
final TaskStack imeTargetStack = getImeFocusStackLocked();
final int imeDockSide = (dockVisible && imeTargetStack != null) ?
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9dae0f2..37d6faf5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,7 +23,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.AppWindowAnimator.sDummyAnimation;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -1328,33 +1327,6 @@
return;
}
- // We crop wallpaper windows to the stack bounds of their current target to avoid them
- // showing behind transparent windows in other stacks in split-screen mode.
- if (w.getBaseType() == TYPE_WALLPAPER) {
- final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
- if (wallpaperTarget != null) {
- final Task task = wallpaperTarget.getTask();
- final WindowStateAnimator winAnimator = wallpaperTarget.mWinAnimator;
- // We can only crop the wallpaper using final crop with stack bounds if the target
- // is not animating, or if it's animating with clip mode STACK_CLIP_AFTER_ANIM.
- // If it's animating with mode STACK_CLIP_NONE, we shouldn't crop either the task
- // itself or the wallpaper. If it's animating with STACK_CLIP_BEFORE_ANIM, the crop
- // is before the transform on the task itself.
- final boolean useFinalCropOnWallpaper = !winAnimator.isAnimationSet()
- || winAnimator.resolveStackClip() == STACK_CLIP_AFTER_ANIM;
- if (task != null && !task.isFullscreen()
- && task.cropWindowsToStackBounds()
- && useFinalCropOnWallpaper){
- final TaskStack stack = task.mStack;
- if (stack != null && !stack.isFullscreen()) {
- stack.getDimBounds(mTmpStackBounds);
- finalClipRect.set(mTmpStackBounds);
- }
- }
- }
- return;
- }
-
final Task task = w.getTask();
if (task == null || !task.cropWindowsToStackBounds()) {
return;
@@ -1487,7 +1459,7 @@
// We need to ensure for each surface, that we disable transformation matrix
// scaling in the same transaction which we resize the surface in.
// As we are in SCALING_MODE_SCALE_TO_WINDOW, SurfaceFlinger will
- // then take over the scaling until the new buffer arrives, and things
+ // then take over the scaling until the new buffer arrives, and things
// will be seamless.
mForceScaleUntilResize = true;
} else {
@@ -1773,6 +1745,10 @@
mWin.mAppToken.onFirstWindowDrawn(mWin, this);
}
+ if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+ mWin.mDisplayContent.mDividerControllerLocked.resetImeHideRequested();
+ }
+
return true;
}
return false;
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 538e8f8..485f2f5 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -597,12 +597,13 @@
private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
// Here's a basic summary of what the IPv4 filter program does:
//
- // if it's multicast and we're dropping multicast:
- // drop
- // if it's not broadcast:
- // pass
- // if it's not DHCP destined to our MAC:
- // drop
+ // if filtering multicast (i.e. multicast lock not held):
+ // if it's multicast:
+ // drop
+ // if it's not broadcast:
+ // pass
+ // if it's not DHCP destined to our MAC:
+ // drop
// pass
if (mMulticastFilter) {
@@ -610,27 +611,27 @@
gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
gen.addAnd(0xf0);
gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
- }
- // Drop all broadcasts besides DHCP addressed to us
- // If not a broadcast packet, pass
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
- // If not UDP, drop
- gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
- gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
- // If fragment, drop. This matches the BPF filter installed by the DHCP client.
- gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
- gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
- // If not to DHCP client port, drop
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
- gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
- // If not DHCP to our MAC address, drop
- gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
- // NOTE: Relies on R1 containing IPv4 header offset.
- gen.addAddR1();
- gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
+ // Drop all broadcasts besides DHCP addressed to us
+ // If not a broadcast packet, pass
+ gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+ // If not UDP, drop
+ gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+ gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
+ // If fragment, drop. This matches the BPF filter installed by the DHCP client.
+ gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
+ gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
+ // If not to DHCP client port, drop
+ gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
+ gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
+ // If not DHCP to our MAC address, drop
+ gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
+ // NOTE: Relies on R1 containing IPv4 header offset.
+ gen.addAddR1();
+ gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
+ }
// Otherwise, pass
gen.addJump(gen.PASS_LABEL);
diff --git a/services/tests/servicestests/res/values/strings.xml b/services/tests/servicestests/res/values/strings.xml
new file mode 100644
index 0000000..2f9d06c
--- /dev/null
+++ b/services/tests/servicestests/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="shortcut_title1"></string>
+ <string name="shortcut_text1"></string>
+ <string name="shortcut_disabled_message1"></string>
+ <string name="shortcut_title2"></string>
+ <string name="shortcut_text2"></string>
+ <string name="shortcut_disabled_message2"></string>
+</resources>
diff --git a/services/tests/servicestests/res/xml/shortcut_0.xml b/services/tests/servicestests/res/xml/shortcut_0.xml
new file mode 100644
index 0000000..fda001e
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_0.xml
@@ -0,0 +1,2 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_1.xml b/services/tests/servicestests/res/xml/shortcut_1.xml
new file mode 100644
index 0000000..c370e74
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_1.xml
@@ -0,0 +1,14 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="true"
+ android:shortcutRank="1"
+ android:shortcutIcon="@drawable/icon1"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutText="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
+ android:shortcutIntentAction="action1"
+ android:shortcutIntentData="data1"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_1_disable.xml b/services/tests/servicestests/res/xml/shortcut_1_disable.xml
new file mode 100644
index 0000000..08ecac3
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_1_disable.xml
@@ -0,0 +1,11 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="false"
+ android:shortcutRank="1"
+ android:shortcutIcon="@drawable/icon2"
+ android:shortcutTitle="@string/shortcut_title2"
+ android:shortcutText="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_2.xml b/services/tests/servicestests/res/xml/shortcut_2.xml
new file mode 100644
index 0000000..9e923f3
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_2.xml
@@ -0,0 +1,26 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="true"
+ android:shortcutRank="1"
+ android:shortcutIcon="@drawable/icon1"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutText="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
+ android:shortcutIntentAction="action1"
+ android:shortcutIntentData="http://a.b.c/"
+ />
+ <shortcut
+ android:shortcutId="ms2"
+ android:enabled="true"
+ android:shortcutRank="2"
+ android:shortcutIcon="@drawable/icon2"
+ android:shortcutTitle="@string/shortcut_title2"
+ android:shortcutText="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ android:shortcutCategories="android.shortcut.conversation"
+ android:shortcutIntentAction="action2"
+ android:shortcutIntentData="http://a.b.c/2"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml b/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml
new file mode 100644
index 0000000..d90c18d
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml
@@ -0,0 +1,12 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutIntentAction="action1"
+ />
+ <shortcut
+ android:shortcutId="ms1"
+ android:shortcutTitle="@string/shortcut_title2"
+ android:shortcutIntentAction="action2"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_5.xml b/services/tests/servicestests/res/xml/shortcut_5.xml
new file mode 100644
index 0000000..f3f71d2
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_5.xml
@@ -0,0 +1,40 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="ms1"
+ android:enabled="true"
+ android:shortcutRank="1"
+ android:shortcutIcon="@drawable/icon1"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutText="@string/shortcut_text1"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
+ android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
+ android:shortcutIntentAction="action1"
+ android:shortcutIntentData="http://a.b.c/1"
+ />
+ <shortcut
+ android:shortcutId="ms2"
+ android:enabled="true"
+ android:shortcutRank="2"
+ android:shortcutIcon="@drawable/icon2"
+ android:shortcutTitle="@string/shortcut_title2"
+ android:shortcutText="@string/shortcut_text2"
+ android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
+ android:shortcutCategories="android.shortcut.conversation"
+ android:shortcutIntentAction="action2"
+ />
+ <shortcut
+ android:shortcutId="ms3"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutIntentAction="android.intent.action.VIEW"
+ />
+ <shortcut
+ android:shortcutId="ms4"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutIntentAction="android.intent.action.VIEW"
+ />
+ <shortcut
+ android:shortcutId="ms5"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutIntentAction="android.intent.action.VIEW"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_1.xml b/services/tests/servicestests/res/xml/shortcut_error_1.xml
new file mode 100644
index 0000000..2c51420
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_error_1.xml
@@ -0,0 +1,11 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutIntentAction="android.intent.action.VIEW"
+ />
+ <shortcut
+ android:shortcutId="x1"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutIntentAction="android.intent.action.VIEW"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_2.xml b/services/tests/servicestests/res/xml/shortcut_error_2.xml
new file mode 100644
index 0000000..d075e7d
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_error_2.xml
@@ -0,0 +1,11 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="manifest-shortcut-3"
+ android:shortcutIntentAction="android.intent.action.VIEW"
+ />
+ <shortcut
+ android:shortcutId="x2"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutIntentAction="android.intent.action.VIEW"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_3.xml b/services/tests/servicestests/res/xml/shortcut_error_3.xml
new file mode 100644
index 0000000..30bf56e
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_error_3.xml
@@ -0,0 +1,11 @@
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+ <shortcut
+ android:shortcutId="manifest-shortcut-3"
+ android:shortcutTitle="@string/shortcut_title1"
+ />
+ <shortcut
+ android:shortcutId="x3"
+ android:shortcutTitle="@string/shortcut_title1"
+ android:shortcutIntentAction="android.intent.action.VIEW"
+ />
+</shortcuts>
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index 9e04d23..fae82ca 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -663,7 +663,7 @@
@LargeTest
public void testApfFilterIPv4() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, false /* multicastFilter */);
+ ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
byte[] program = ipManagerCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
@@ -726,46 +726,57 @@
byte[] program = ipManagerCallback.getApfProgram();
// Construct IPv4 and IPv6 multicast packets.
- ByteBuffer v4packet = ByteBuffer.wrap(new byte[100]);
- v4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- v4packet.position(IPV4_DEST_ADDR_OFFSET);
- v4packet.put(new byte[]{(byte)224,0,0,1});
+ ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
+ mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ mcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
+ mcastv4packet.put(new byte[]{(byte)224,0,0,1});
- ByteBuffer v6packet = ByteBuffer.wrap(new byte[100]);
- v6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
- v6packet.position(IPV6_DEST_ADDR_OFFSET);
- v6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+ ByteBuffer mcastv6packet = ByteBuffer.wrap(new byte[100]);
+ mcastv6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
+ mcastv6packet.position(IPV6_DEST_ADDR_OFFSET);
+ mcastv6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+
+ // Construct IPv4 broadcast packet.
+ ByteBuffer bcastv4packet = ByteBuffer.wrap(new byte[100]);
+ bcastv4packet.put(ETH_BROADCAST_MAC_ADDRESS);
+ bcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ bcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
+ bcastv4packet.put(new byte[]{(byte)192,(byte)0,(byte)2,(byte)63});
// Verify initially disabled multicast filter is off
- assertPass(program, v4packet.array(), 0);
- assertPass(program, v6packet.array(), 0);
+ assertPass(program, bcastv4packet.array(), 0);
+ assertPass(program, mcastv4packet.array(), 0);
+ assertPass(program, mcastv6packet.array(), 0);
// Turn on multicast filter and verify it works
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(true);
program = ipManagerCallback.getApfProgram();
- assertDrop(program, v4packet.array(), 0);
- assertDrop(program, v6packet.array(), 0);
+ assertDrop(program, bcastv4packet.array(), 0);
+ assertDrop(program, mcastv4packet.array(), 0);
+ assertDrop(program, mcastv6packet.array(), 0);
// Turn off multicast filter and verify it's off
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(false);
program = ipManagerCallback.getApfProgram();
- assertPass(program, v4packet.array(), 0);
- assertPass(program, v6packet.array(), 0);
+ assertPass(program, bcastv4packet.array(), 0);
+ assertPass(program, mcastv4packet.array(), 0);
+ assertPass(program, mcastv6packet.array(), 0);
// Verify it can be initialized to on
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, true /* multicastFilter */);
program = ipManagerCallback.getApfProgram();
- assertDrop(program, v4packet.array(), 0);
- assertDrop(program, v6packet.array(), 0);
+ assertDrop(program, bcastv4packet.array(), 0);
+ assertDrop(program, mcastv4packet.array(), 0);
+ assertDrop(program, mcastv6packet.array(), 0);
// Verify that ICMPv6 multicast is not dropped.
- v6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- assertPass(program, v6packet.array(), 0);
+ mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+ assertPass(program, mcastv6packet.array(), 0);
apfFilter.shutdown();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 8a2a58c..1a0a003 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -15,17 +15,22 @@
*/
package com.android.server.pm;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDisabled;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamic;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllDynamicOrPinned;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllEnabled;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIcon;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIconFile;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIconResId;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveIntents;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllHaveTitle;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllImmutable;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllKeyFieldsOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllManifest;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveIntents;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotHaveTitle;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotKeyFieldsOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllNotManifest;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllPinned;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllStringsResolved;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertAllUnique;
@@ -33,8 +38,10 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertBundleEmpty;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCallbackNotReceived;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCallbackReceived;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertCannotUpdateImmutable;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicAndPinned;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertDynamicOnly;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertEmpty;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertShortcutIds;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.findShortcut;
@@ -70,6 +77,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ILauncherApps;
import android.content.pm.LauncherApps;
@@ -83,6 +91,7 @@
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
@@ -143,6 +152,11 @@
adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest \
-w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+
+ * TODO More tests for pinning + manifest shortcuts
+ * TODO Manifest shortcuts + app upgrade -> launcher callback.
+ * Also locale change should trigger launcehr callbacks too, when they use strign resoucres.
+ * (not implemented yet.)
* TODO: Add checks with assertAllNotHaveIcon()
* TODO: Detailed test for hasShortcutPermissionInner().
* TODO: Add tests for the command line functions too.
@@ -155,9 +169,9 @@
* Whether to enable dump or not. Should be only true when debugging to avoid bugs where
* dump affecting the behavior.
*/
- private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true
+ private static final boolean ENABLE_DUMP = true; // DO NOT SUBMIT WITH true
- private static final boolean DUMP_IN_TEARDOWN = false; // DO NOT SUBMIT WITH true
+ private static final boolean DUMP_IN_TEARDOWN = true; // DO NOT SUBMIT WITH true
private static final String[] EMPTY_STRINGS = new String[0]; // Just for readability.
@@ -229,6 +243,15 @@
public int getUserId() {
return UserHandle.USER_SYSTEM;
}
+
+ public PackageInfo injectGetActivitiesWithMetadata(
+ String packageName, @UserIdInt int userId) {
+ return ShortcutManagerTest.this.injectGetActivitiesWithMetadata(packageName, userId);
+ }
+
+ public XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
+ return ShortcutManagerTest.this.injectXmlMetaData(activityInfo, key);
+ }
}
/** ShortcutService with injection override methods. */
@@ -339,6 +362,21 @@
}
@Override
+ List<ApplicationInfo> injectInstalledApplications(@UserIdInt int userId) {
+ return getInstalledApplications(userId);
+ }
+
+ @Override
+ PackageInfo injectGetActivitiesWithMetadata(String packageName, @UserIdInt int userId) {
+ return mContext.injectGetActivitiesWithMetadata(packageName, userId);
+ }
+
+ @Override
+ XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
+ return mContext.injectXmlMetaData(activityInfo, key);
+ }
+
+ @Override
void postToHandler(Runnable r) {
final long token = mContext.injectClearCallingIdentity();
r.run();
@@ -524,6 +562,9 @@
private final ArrayList<String> mCallerPermissions = new ArrayList<>();
+ private final HashMap<String, HashMap<ComponentName, Integer>> mActivityMetadataResId
+ = new HashMap<>();
+
static {
QUERY_ALL.setQueryFlags(
ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_PINNED);
@@ -720,6 +761,12 @@
});
}
+ private void updatePackageLastUpdateTime(String packageName, long increment) {
+ updatePackageInfo(packageName, pi -> {
+ pi.lastUpdateTime += increment;
+ });
+ }
+
private void uninstallPackage(int userId, String packageName) {
if (ENABLE_DUMP) {
Log.i(TAG, "Unnstall package " + packageName + " / " + userId);
@@ -742,8 +789,12 @@
final PackageInfo ret = new PackageInfo();
ret.packageName = pi.packageName;
ret.versionCode = pi.versionCode;
+ ret.lastUpdateTime = pi.lastUpdateTime;
+
ret.applicationInfo = new ApplicationInfo(pi.applicationInfo);
ret.applicationInfo.uid = UserHandle.getUid(userId, pi.applicationInfo.uid);
+ ret.applicationInfo.packageName = pi.packageName;
+
if (mUninstalledPackages.contains(PackageWithUser.of(userId, packageName))) {
ret.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
}
@@ -755,6 +806,66 @@
return ret;
}
+ private void addApplicationInfo(PackageInfo pi, List<ApplicationInfo> list) {
+ if (pi != null && pi.applicationInfo != null) {
+ list.add(pi.applicationInfo);
+ }
+ }
+
+ private List<ApplicationInfo> getInstalledApplications(int userId) {
+ final ArrayList<ApplicationInfo> ret = new ArrayList<>();
+
+ addApplicationInfo(getInjectedPackageInfo(CALLING_PACKAGE_1, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(CALLING_PACKAGE_2, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(CALLING_PACKAGE_3, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(CALLING_PACKAGE_4, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(LAUNCHER_1, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(LAUNCHER_2, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(LAUNCHER_3, userId, false), ret);
+ addApplicationInfo(getInjectedPackageInfo(LAUNCHER_4, userId, false), ret);
+
+ return ret;
+ }
+
+ private void addManifestShortcutResource(ComponentName activity, int resId) {
+ final String packageName = activity.getPackageName();
+ HashMap<ComponentName, Integer> map = mActivityMetadataResId.get(packageName);
+ if (map == null) {
+ map = new HashMap<>();
+ mActivityMetadataResId.put(packageName, map);
+ }
+ map.put(activity, resId);
+ }
+
+ private PackageInfo injectGetActivitiesWithMetadata(String packageName, @UserIdInt int userId) {
+ final PackageInfo ret = getInjectedPackageInfo(packageName, userId,
+ /* getSignatures=*/ false);
+
+ final HashMap<ComponentName, Integer> activities = mActivityMetadataResId.get(packageName);
+ if (activities != null) {
+ final ArrayList<ActivityInfo> list = new ArrayList<>();
+
+ for (ComponentName cn : activities.keySet()) {
+ ActivityInfo ai = new ActivityInfo();
+ ai.packageName = cn.getPackageName();
+ ai.name = cn.getClassName();
+ ai.metaData = new Bundle();
+ ai.metaData.putInt(ShortcutParser.METADATA_KEY, activities.get(cn));
+ list.add(ai);
+ }
+ ret.activities = list.toArray(new ActivityInfo[list.size()]);
+ }
+ return ret;
+ }
+
+ private XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
+ if (!ShortcutParser.METADATA_KEY.equals(key) || activityInfo.metaData == null) {
+ return null;
+ }
+ final int resId = activityInfo.metaData.getInt(key);
+ return getTestContext().getResources().getXml(resId);
+ }
+
/** Replace the current calling package */
private void setCaller(String packageName, int userId) {
mInjectedClientPackage = packageName;
@@ -883,6 +994,12 @@
makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
}
+ private ShortcutInfo makeShortcutWithTitle(String id, String title) {
+ return makeShortcut(
+ id, title, /* activity =*/ null, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* weight =*/ 0);
+ }
+
/**
* Make a shortcut with an ID and timestamp.
*/
@@ -949,7 +1066,7 @@
Icon icon, Intent intent, int rank) {
final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext)
.setId(id)
- .setActivityComponent(new ComponentName(mClientContext.getPackageName(), "dummy"))
+ .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy"))
.setTitle(title)
.setRank(rank)
.setIntent(intent);
@@ -957,7 +1074,7 @@
b.setIcon(icon);
}
if (activity != null) {
- b.setActivityComponent(activity);
+ b.setActivity(activity);
}
final ShortcutInfo s = b.build();
@@ -1126,8 +1243,10 @@
return new File(si.getBitmapPath()).getName();
}
- private ShortcutInfo getPackageShortcut(String packageName, String shortcutId) {
- return getPackageShortcut(packageName, shortcutId, getCallingUserId());
+ private List<ShortcutInfo> getCallerShortcuts() {
+ final ShortcutPackage p = mService.getPackageShortcutForTest(
+ getCallingPackage(), getCallingUserId());
+ return p == null ? null : p.getAllShortcutsForTest();
}
private ShortcutInfo getCallerShortcut(String shortcutId) {
@@ -1157,6 +1276,13 @@
return infoList.get(0);
}
+ private Intent genPackageAddIntent(String pakcageName, int userId) {
+ Intent i = new Intent(Intent.ACTION_PACKAGE_ADDED);
+ i.setData(Uri.parse("package:" + pakcageName));
+ i.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ return i;
+ }
+
private Intent genPackageDeleteIntent(String pakcageName, int userId) {
Intent i = new Intent(Intent.ACTION_PACKAGE_REMOVED);
i.setData(Uri.parse("package:" + pakcageName));
@@ -1291,9 +1417,9 @@
// === Test for app side APIs ===
- /** Test for {@link android.content.pm.ShortcutManager#getMaxDynamicShortcutCount()} */
+ /** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */
public void testGetMaxDynamicShortcutCount() {
- assertEquals(MAX_SHORTCUTS, mManager.getMaxDynamicShortcutCount());
+ assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity());
}
/** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
@@ -1301,6 +1427,11 @@
assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount());
}
+ public void getIconMaxDimensions() {
+ assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxWidth());
+ assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxHeight());
+ }
+
/** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
public void testGetRateLimitResetTime() {
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
@@ -2393,7 +2524,7 @@
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
.setId("id")
- .setActivityComponent(new ComponentName(mClientContext, "dummy"))
+ .setActivity(new ComponentName(mClientContext, "dummy"))
.setTitleResId(10)
.setTextResId(11)
.setDisabledMessageResId(12)
@@ -2404,7 +2535,7 @@
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
.setId("id")
- .setActivityComponent(new ComponentName(mClientContext, "dummy"))
+ .setActivity(new ComponentName(mClientContext, "dummy"))
.setTitleResId(10)
.setTextResId(11)
.setDisabledMessageResId(12)
@@ -2514,7 +2645,6 @@
}
public void testPinShortcutAndGetPinnedShortcuts() {
- // Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2551,38 +2681,155 @@
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
mManager.removeDynamicShortcuts(list("s2"));
assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s1");
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
mManager.removeDynamicShortcuts(list("s3"));
assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s2", "s4");
});
runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
mManager.removeDynamicShortcuts(list("s2"));
assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+
+ assertEmpty(mManager.getDynamicShortcuts());
});
// Get pinned shortcuts from launcher
runWithCaller(LAUNCHER_1, USER_0, () -> {
// CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
- assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
- /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))),
"s2");
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))),
+ "s3", "s4");
+
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))))
+ /* none */);
+ });
+ }
+
+ /**
+ * This is similar to the above test, except it used "disable" instead of "remove". It also
+ * does "enable".
+ */
+ public void testDisableAndEnableShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
+ final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
+
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
+ final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
+ final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
+ assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
+ assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
+ });
+
+ // Pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2", "s3"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3", "s4", "s5"), getCallingUser());
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
+ list("s3"), getCallingUser()); // Note ID doesn't exist
+ });
+
+ // Disable some.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+
+ mManager.disableShortcuts(list("s2"));
+
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s1");
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+
+ // disable should work even if a shortcut is not dynamic, so try calling "remove" first
+ // here.
+ mManager.removeDynamicShortcuts(list("s3"));
+ mManager.disableShortcuts(list("s3"));
+
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s3", "s4");
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s2", "s4");
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+
+ mManager.disableShortcuts(list("s2"));
+
+ assertShortcutIds(mManager.getPinnedShortcuts() /* none */);
+
+ assertEmpty(mManager.getDynamicShortcuts());
+ assertEmpty(getCallerShortcuts());
+ });
+
+ // Get pinned shortcuts from launcher
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllDisabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))),
+ "s2");
+ assertFalse(mLauncherApps.startShortcut(
+ CALLING_PACKAGE_1, "s2", null, null, HANDLE_USER_0));
+
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
/* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))),
"s3", "s4");
+ assertFalse(mLauncherApps.startShortcut(
+ CALLING_PACKAGE_2, "s3", null, null, HANDLE_USER_0));
+ assertTrue(mLauncherApps.startShortcut(
+ CALLING_PACKAGE_2, "s4", null, null, HANDLE_USER_0));
- assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_3,
- /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser()))))
/* none */);
});
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.enableShortcuts(list("s2"));
+
+ assertShortcutIds(mManager.getPinnedShortcuts(), "s2");
+ assertShortcutIds(mManager.getDynamicShortcuts(), "s1");
+ });
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ // CALLING_PACKAGE_1 deleted s2, but it's pinned, so it still exists.
+ assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(assertAllEnabled(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), getCallingUser())))),
+ "s2");
+ assertTrue(mLauncherApps.startShortcut(
+ CALLING_PACKAGE_1, "s2", null, null, HANDLE_USER_0));
+ });
}
public void testPinShortcutAndGetPinnedShortcuts_multi() {
@@ -6007,14 +6254,14 @@
() -> new ShortcutInfo.Builder(getTestContext()).setIntent(new Intent()));
assertExpectException(
NullPointerException.class,
- "activityComponent must be provided",
+ "activity must be provided",
() -> new ShortcutInfo.Builder(getTestContext()).setId("id").build()
.enforceMandatoryFields());
assertExpectException(
IllegalArgumentException.class,
"title must be provided",
() -> new ShortcutInfo.Builder(getTestContext()).setId("id")
- .setActivityComponent(
+ .setActivity(
new ComponentName(getTestContext().getPackageName(), "s"))
.build()
.enforceMandatoryFields());
@@ -6022,7 +6269,7 @@
NullPointerException.class,
"Intent must be provided",
() -> new ShortcutInfo.Builder(getTestContext()).setId("id")
- .setActivityComponent(
+ .setActivity(
new ComponentName(getTestContext().getPackageName(), "s"))
.setTitle("x").build()
.enforceMandatoryFields());
@@ -6035,7 +6282,7 @@
.setTitle("title")
.setIntent(makeIntent("action", ShortcutActivity.class))
.build());
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals(USER_10, si.getUserId());
assertEquals(HANDLE_USER_10, si.getUserHandle());
assertEquals("id", si.getId());
@@ -6047,7 +6294,7 @@
si = new ShortcutInfo.Builder(getTestContext())
.setId("id")
- .setActivityComponent(new ComponentName("a", "b"))
+ .setActivity(new ComponentName("a", "b"))
.setIcon(Icon.createWithResource(mClientContext, 123))
.setTitle("title")
.setText("text")
@@ -6063,9 +6310,9 @@
si = parceled(si);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(123, si.getIcon().getResId());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
@@ -6090,7 +6337,7 @@
si = new ShortcutInfo.Builder(getTestContext())
.setId("id")
- .setActivityComponent(new ComponentName("a", "b"))
+ .setActivity(new ComponentName("a", "b"))
.setIcon(Icon.createWithResource(mClientContext, 123))
.setTitleResId(10)
.setTextResId(11)
@@ -6106,9 +6353,9 @@
si = parceled(si);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(123, si.getIcon().getResId());
assertEquals(10, si.getTitleResId());
assertEquals(11, si.getTextResId());
@@ -6131,7 +6378,7 @@
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
- .setActivityComponent(new ComponentName("a", "b"))
+ .setActivity(new ComponentName("a", "b"))
.setIcon(Icon.createWithResource(mClientContext, 123))
.setTitle("title")
.setText("text")
@@ -6149,9 +6396,9 @@
assertEquals(USER_11, si.getUserId());
assertEquals(HANDLE_USER_11, si.getUserHandle());
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(123, si.getIcon().getResId());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
@@ -6168,9 +6415,9 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
@@ -6188,9 +6435,9 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
@@ -6207,9 +6454,9 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(null, si.getActivityComponent());
+ assertEquals(null, si.getActivity());
assertEquals(null, si.getIcon());
assertEquals(null, si.getTitle());
assertEquals(null, si.getText());
@@ -6232,7 +6479,7 @@
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
- .setActivityComponent(new ComponentName("a", "b"))
+ .setActivity(new ComponentName("a", "b"))
.setIcon(Icon.createWithResource(mClientContext, 123))
.setTitleResId(10)
.setTextResId(11)
@@ -6250,9 +6497,9 @@
assertEquals(USER_11, si.getUserId());
assertEquals(HANDLE_USER_11, si.getUserHandle());
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(123, si.getIcon().getResId());
assertEquals(10, si.getTitleResId());
assertEquals(11, si.getTextResId());
@@ -6269,9 +6516,9 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(null, si.getIcon());
assertEquals(10, si.getTitleResId());
assertEquals(11, si.getTextResId());
@@ -6289,9 +6536,9 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(new ComponentName("a", "b"), si.getActivityComponent());
+ assertEquals(new ComponentName("a", "b"), si.getActivity());
assertEquals(null, si.getIcon());
assertEquals(10, si.getTitleResId());
assertEquals(11, si.getTextResId());
@@ -6308,9 +6555,9 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
- assertEquals(mClientContext.getPackageName(), si.getPackageName());
+ assertEquals(mClientContext.getPackageName(), si.getPackage());
assertEquals("id", si.getId());
- assertEquals(null, si.getActivityComponent());
+ assertEquals(null, si.getActivity());
assertEquals(null, si.getIcon());
assertEquals(0, si.getTitleResId());
assertEquals(0, si.getTextResId());
@@ -6336,7 +6583,7 @@
.build();
ShortcutInfo si = sorig.clone(/* clone flags*/ 0);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
assertEquals("id", si.getId());
assertEquals("title", si.getTitle());
assertEquals("action", si.getIntent().getAction());
@@ -6344,7 +6591,7 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_CREATOR);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
assertEquals("id", si.getId());
assertEquals("title", si.getTitle());
assertEquals("action", si.getIntent().getAction());
@@ -6352,7 +6599,7 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
assertEquals("id", si.getId());
assertEquals("title", si.getTitle());
assertEquals(null, si.getIntent());
@@ -6360,7 +6607,7 @@
si = sorig.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
- assertEquals(getTestContext().getPackageName(), si.getPackageName());
+ assertEquals(getTestContext().getPackageName(), si.getPackage());
assertEquals("id", si.getId());
assertEquals(null, si.getTitle());
assertEquals(null, si.getIntent());
@@ -6372,7 +6619,7 @@
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
.setId("id")
- .setActivityComponent(new ComponentName("a", "b"))
+ .setActivity(new ComponentName("a", "b"))
.setIcon(Icon.createWithResource(mClientContext, 123))
.setTitle("title")
.setText("text")
@@ -6390,9 +6637,9 @@
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
- .setActivityComponent(new ComponentName("x", "y")).build());
+ .setActivity(new ComponentName("x", "y")).build());
assertEquals("text", si.getText());
- assertEquals(new ComponentName("x", "y"), si.getActivityComponent());
+ assertEquals(new ComponentName("x", "y"), si.getActivity());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -6500,7 +6747,7 @@
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(getTestContext())
.setId("id")
- .setActivityComponent(new ComponentName("a", "b"))
+ .setActivity(new ComponentName("a", "b"))
.setIcon(Icon.createWithResource(mClientContext, 123))
.setTitleResId(10)
.setTextResId(11)
@@ -6518,9 +6765,9 @@
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
- .setActivityComponent(new ComponentName("x", "y")).build());
+ .setActivity(new ComponentName("x", "y")).build());
assertEquals(11, si.getTextResId());
- assertEquals(new ComponentName("x", "y"), si.getActivityComponent());
+ assertEquals(new ComponentName("x", "y"), si.getActivity());
si = sorig.clone(/* flags=*/ 0);
si.copyNonNullFieldsFrom(new ShortcutInfo.Builder(getTestContext()).setId("id")
@@ -6633,7 +6880,7 @@
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
- .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
.setIcon(bmp32x32)
.setTitle("title")
.setText("text")
@@ -6659,9 +6906,9 @@
assertEquals(USER_10, si.getUserId());
assertEquals(HANDLE_USER_10, si.getUserHandle());
- assertEquals(CALLING_PACKAGE_1, si.getPackageName());
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
assertEquals("id", si.getId());
- assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
@@ -6688,7 +6935,7 @@
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
- .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
.setIcon(bmp32x32)
.setTitleResId(10)
.setTextResId(11)
@@ -6714,9 +6961,9 @@
assertEquals(USER_10, si.getUserId());
assertEquals(HANDLE_USER_10, si.getUserHandle());
- assertEquals(CALLING_PACKAGE_1, si.getPackageName());
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
assertEquals("id", si.getId());
- assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
assertEquals(null, si.getIcon());
assertEquals(10, si.getTitleResId());
assertEquals(11, si.getTextResId());
@@ -6743,7 +6990,7 @@
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
- .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
.setIcon(bmp32x32)
.setTitle("title")
.setText("text")
@@ -6768,9 +7015,9 @@
ShortcutInfo si;
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
- assertEquals(CALLING_PACKAGE_1, si.getPackageName());
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
assertEquals("id", si.getId());
- assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
assertEquals(null, si.getIcon());
assertEquals("title", si.getTitle());
assertEquals("text", si.getText());
@@ -6796,7 +7043,7 @@
pb.putInt("k", 1);
ShortcutInfo sorig = new ShortcutInfo.Builder(mClientContext)
.setId("id")
- .setActivityComponent(new ComponentName(mClientContext, ShortcutActivity2.class))
+ .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
.setIcon(bmp32x32)
.setTitleResId(10)
.setTextResId(11)
@@ -6821,9 +7068,9 @@
ShortcutInfo si;
si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_0);
- assertEquals(CALLING_PACKAGE_1, si.getPackageName());
+ assertEquals(CALLING_PACKAGE_1, si.getPackage());
assertEquals("id", si.getId());
- assertEquals(ShortcutActivity2.class.getName(), si.getActivityComponent().getClassName());
+ assertEquals(ShortcutActivity2.class.getName(), si.getActivity().getClassName());
assertEquals(null, si.getIcon());
assertEquals(10, si.getTitleResId());
assertEquals(11, si.getTextResId());
@@ -6849,4 +7096,810 @@
// Dump after having some icons.
dumpsysOnLogcat("test1", /* force= */ true);
}
+
+ public void testManifestShortcut_publishOnUnlockUser() {
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+
+ // Unlock user-0.
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Try on another user, with some packages uninstalled.
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ uninstallPackage(USER_10, CALLING_PACKAGE_3);
+
+ mService.handleUnlockUser(USER_10);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_10, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Now change the resources for package 1, and unlock again.
+ // But we still see *old* shortcuts, because the package version and install time
+ // hasn't changed.
+ shutdownServices();
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Do it again, but this time we change the app version, so we do detect the changes.
+ shutdownServices();
+
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ updatePackageLastUpdateTime(CALLING_PACKAGE_3, 1);
+
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Next, try removing all shortcuts, with some of them pinned.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms3"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("ms2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_3, list("ms1"), HANDLE_USER_0);
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllManifest(
+ assertAllEnabled(mManager.getPinnedShortcuts())))),
+ "ms3");
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllManifest(
+ assertAllEnabled(mManager.getPinnedShortcuts())))),
+ "ms2");
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllManifest(
+ assertAllEnabled(mManager.getPinnedShortcuts())))),
+ "ms1");
+ });
+
+ shutdownServices();
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_0);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_3, ShortcutActivity.class.getName()),
+ R.xml.shortcut_0);
+
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ updatePackageVersion(CALLING_PACKAGE_2, 1);
+ updatePackageVersion(CALLING_PACKAGE_3, 1);
+
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllNotManifest(
+ assertAllDisabled(mManager.getPinnedShortcuts())))),
+ "ms3");
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllNotManifest(
+ assertAllDisabled(mManager.getPinnedShortcuts())))),
+ "ms2");
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllNotManifest(
+ assertAllDisabled(mManager.getPinnedShortcuts())))),
+ "ms1");
+ });
+
+ // Make sure we don't have ShortcutPackage for packages that don't have shortcuts.
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_4, USER_0));
+ assertNull(mService.getPackageShortcutForTest(LAUNCHER_1, USER_0));
+ }
+
+
+ public void testManifestShortcut_publishOnBroadcast() {
+ // First, no packages are installed.
+ uninstallPackage(USER_0, CALLING_PACKAGE_1);
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+ uninstallPackage(USER_0, CALLING_PACKAGE_3);
+ uninstallPackage(USER_0, CALLING_PACKAGE_4);
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ uninstallPackage(USER_10, CALLING_PACKAGE_2);
+ uninstallPackage(USER_10, CALLING_PACKAGE_3);
+ uninstallPackage(USER_10, CALLING_PACKAGE_4);
+
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_10);
+
+ // Originally no manifest shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Package 1 updated, with manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Package 2 updated, with manifest shortcuts.
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_2, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Package 2 updated, with less manifest shortcuts.
+ // This time we use updatePackageLastUpdateTime() instead of updatePackageVersion().
+
+ dumpsysOnLogcat("Before pinning");
+
+ // Also pin some.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("ms2", "ms3"), HANDLE_USER_0);
+ });
+
+ dumpsysOnLogcat("After pinning");
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(
+ mManager.getPinnedShortcuts())),
+ "ms2", "ms3");
+ // ms3 is no longer in manifest, so should be disabled.
+ // but ms1 and ms2 should be enabled.
+ assertAllEnabled(list(getCallerShortcut("ms1")));
+ assertAllEnabled(list(getCallerShortcut("ms2")));
+ assertAllDisabled(list(getCallerShortcut("ms3")));
+ });
+
+ // Package 2 on user 10 has no shortcuts yet.
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+ // Send PACKAGE_ADD broadcast to have Package 2 on user-10 publish manifest shortcuts.
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // But it shouldn't affect user-0.
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+ assertShortcutIds(assertAllImmutable(assertAllPinned(
+ mManager.getPinnedShortcuts())),
+ "ms2", "ms3");
+ assertAllEnabled(list(getCallerShortcut("ms1")));
+ assertAllEnabled(list(getCallerShortcut("ms2")));
+ assertAllDisabled(list(getCallerShortcut("ms3")));
+ });
+
+ // Package 2 now has no manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
+ R.xml.shortcut_0);
+ updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
+
+ // No manifest shortcuts, and pinned ones are disabled.
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertShortcutIds(assertAllImmutable(assertAllPinned(assertAllDisabled(
+ mManager.getPinnedShortcuts()))),
+ "ms2", "ms3");
+ });
+ }
+
+ public void testManifestShortcuts_missingMandatoryFields() {
+ // Start with no apps installed.
+ uninstallPackage(USER_0, CALLING_PACKAGE_1);
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+ uninstallPackage(USER_0, CALLING_PACKAGE_3);
+ uninstallPackage(USER_0, CALLING_PACKAGE_4);
+
+ mService.handleUnlockUser(USER_0);
+
+ // Make sure no manifest shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ });
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_error_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "x1");
+ });
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_error_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "x2");
+ });
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_error_3);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "x3");
+ });
+ }
+
+ public void testManifestShortcuts_checkAllFields() {
+ mService.handleUnlockUser(USER_0);
+
+ // Package 1 updated, which has one valid manifest shortcut and one invalid.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2", "ms3", "ms4", "ms5");
+
+ // check first shortcut.
+ ShortcutInfo si = getCallerShortcut("ms1");
+
+ assertEquals("ms1", si.getId());
+ assertEquals(R.drawable.icon1, si.getIconResourceId());
+ assertEquals(new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ si.getActivity());
+ assertEquals(R.string.shortcut_title1, si.getTitleResId());
+ assertEquals(R.string.shortcut_text1, si.getTextResId());
+ assertEquals(R.string.shortcut_disabled_message1, si.getDisabledMessageResId());
+ assertEquals(set("android.shortcut.conversation", "android.shortcut.media"),
+ si.getCategories());
+ assertEquals("action1", si.getIntent().getAction());
+ assertEquals(Uri.parse("http://a.b.c/1"), si.getIntent().getData());
+
+ // check another
+ si = getCallerShortcut("ms2");
+
+ assertEquals("ms2", si.getId());
+ assertEquals(R.drawable.icon2, si.getIconResourceId());
+ assertEquals(R.string.shortcut_title2, si.getTitleResId());
+ assertEquals(R.string.shortcut_text2, si.getTextResId());
+ assertEquals(R.string.shortcut_disabled_message2, si.getDisabledMessageResId());
+ assertEquals(set("android.shortcut.conversation"), si.getCategories());
+ assertEquals("action2", si.getIntent().getAction());
+ assertEquals(null, si.getIntent().getData());
+
+ // check another
+ si = getCallerShortcut("ms3");
+
+ assertEquals("ms3", si.getId());
+ assertEquals(0, si.getIconResourceId());
+ assertEquals(R.string.shortcut_title1, si.getTitleResId());
+ assertEquals(0, si.getTextResId());
+ assertEquals(0, si.getDisabledMessageResId());
+ assertEquals(null, si.getCategories());
+ assertEquals("android.intent.action.VIEW", si.getIntent().getAction());
+ assertEquals(null, si.getIntent().getData());
+ });
+ }
+
+ public void testManifestShortcuts_updateAndDisabled_notPinned() {
+ mService.handleUnlockUser(USER_0);
+
+ // First, just publish a manifest shortcut.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1");
+ });
+
+ // Now version up, the manifest shortcut is disabled now.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1_disable);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Because shortcut 1 wasn't pinned, it'll just go away.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+
+ // Make sure there's no other dangling shortcuts.
+ assertEmpty(getCallerShortcuts());
+ });
+ }
+
+ public void testManifestShortcuts_updateAndDisabled_pinned() {
+ mService.handleUnlockUser(USER_0);
+
+ // First, just publish a manifest shortcut.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Only the valid one is published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+ assertEmpty(mManager.getPinnedShortcuts());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1");
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_0);
+ });
+
+ // Now upgrade, the manifest shortcut is disabled now.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1_disable);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Because shortcut 1 was pinned, it'll still exist as pinned, but disabled.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertShortcutIds(assertAllNotManifest(assertAllImmutable(assertAllDisabled(
+ mManager.getPinnedShortcuts()))),
+ "ms1");
+
+ // Make sure the fields are updated.
+ ShortcutInfo si = getCallerShortcut("ms1");
+
+ assertEquals("ms1", si.getId());
+ assertEquals(R.drawable.icon2, si.getIconResourceId());
+ assertEquals(R.string.shortcut_title2, si.getTitleResId());
+ assertEquals(R.string.shortcut_text2, si.getTextResId());
+ assertEquals(R.string.shortcut_disabled_message2, si.getDisabledMessageResId());
+ assertEquals(Intent.ACTION_VIEW, si.getIntent().getAction());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1");
+ });
+ }
+
+ public void testManifestShortcuts_duplicateInSingleActivity() {
+ mService.handleUnlockUser(USER_0);
+
+ // The XML has two shortcuts with the same ID.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2_duplicate);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1");
+
+ // Make sure the first one has survived. (the second one has a different title.)
+ ShortcutInfo si = getCallerShortcut("ms1");
+ assertEquals(R.string.shortcut_title1, si.getTitleResId());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1");
+ });
+ }
+
+ public void testManifestShortcuts_duplicateInTwoActivities() {
+ mService.handleUnlockUser(USER_0);
+
+ // ShortcutActivity has shortcut ms1
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+
+ // ShortcutActivity2 has two shortcuts, ms1 and ms2.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
+ mManager.getManifestShortcuts()))),
+ "ms1", "ms2");
+
+ // ms1 should belong to ShortcutActivity.
+ ShortcutInfo si = getCallerShortcut("ms1");
+ assertEquals(R.string.shortcut_title1, si.getTitleResId());
+ assertEquals(new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ si.getActivity());
+
+ // ms2 should belong to ShortcutActivity*2*.
+ si = getCallerShortcut("ms2");
+ assertEquals(R.string.shortcut_title2, si.getTitleResId());
+ assertEquals(new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
+ si.getActivity());
+
+ // Make sure there's no other dangling shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "ms1", "ms2");
+ });
+ }
+
+ /**
+ * Manifest shortcuts cannot override shortcuts that were published via the APIs.
+ */
+ public void testManifestShortcuts_cannotOverrideNonManifest() {
+ mService.handleUnlockUser(USER_0);
+
+ // Create a non-pinned dynamic shortcut and a non-dynamic pinned shortcut.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.setDynamicShortcuts(list(
+ makeShortcut("ms1", "title1",
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ /* icon */ null, new Intent("action1"), /* rank */ 0),
+ makeShortcut("ms2", "title2",
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ /* icon */ null, new Intent("action1"), /* rank */ 0)));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2"), HANDLE_USER_0);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("ms2"));
+
+ assertShortcutIds(mManager.getDynamicShortcuts(), "ms1");
+ assertShortcutIds(mManager.getPinnedShortcuts(), "ms2");
+ assertEmpty(mManager.getManifestShortcuts());
+ });
+
+ // Then update the app with 5 manifest shortcuts.
+ // Make sure "ms1" and "ms2" won't be replaced.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_5);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllNotManifest(mManager.getDynamicShortcuts()), "ms1");
+ assertShortcutIds(assertAllNotManifest(mManager.getPinnedShortcuts()), "ms2");
+ assertShortcutIds(assertAllManifest(mManager.getManifestShortcuts()),
+ "ms3", "ms4", "ms5");
+
+ // ms1 and ms2 shouold keep the original title.
+ ShortcutInfo si = getCallerShortcut("ms1");
+ assertEquals("title1", si.getTitle());
+
+ si = getCallerShortcut("ms2");
+ assertEquals("title2", si.getTitle());
+ });
+ }
+
+ private void checkManifestShortcuts_immutable_verify() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertShortcutIds(assertAllNotManifest(assertAllEnabled(
+ mManager.getDynamicShortcuts())),
+ "s1");
+ assertShortcutIds(assertAllManifest(assertAllEnabled(
+ mManager.getManifestShortcuts())),
+ "ms1");
+ assertShortcutIds(assertAllNotManifest(assertAllDisabled(
+ mManager.getPinnedShortcuts())),
+ "ms2");
+
+ assertEquals("t1", getCallerShortcut("s1").getTitle());
+
+ // Make sure there are no other shortcuts.
+ assertShortcutIds(getCallerShortcuts(), "s1", "ms1", "ms2");
+ });
+ }
+
+ /**
+ * Make sure the APIs won't work on manifest shortcuts.
+ */
+ public void testManifestShortcuts_immutable() {
+ mService.handleUnlockUser(USER_0);
+
+ // Create a non-pinned manifest shortcut, a pinned shortcut that was originally
+ // a manifest shortcut, as well as a dynamic shortcut.
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2"), HANDLE_USER_0);
+ });
+
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.addDynamicShortcuts(list(makeShortcutWithTitle("s1", "t1")));
+ });
+
+ checkManifestShortcuts_immutable_verify();
+
+ // Note that even though the first argument is not immutable and only the second one
+ // is immutable, the first argument should not be executed either.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.setDynamicShortcuts(list(makeShortcut("xx"), makeShortcut("ms1")));
+ });
+ assertCannotUpdateImmutable(() -> {
+ mManager.setDynamicShortcuts(list(makeShortcut("xx"), makeShortcut("ms2")));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.addDynamicShortcuts(list(makeShortcut("xx"), makeShortcut("ms1")));
+ });
+ assertCannotUpdateImmutable(() -> {
+ mManager.addDynamicShortcuts(list(makeShortcut("xx"), makeShortcut("ms2")));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.updateShortcuts(list(makeShortcut("s1"), makeShortcut("ms1")));
+ });
+ assertCannotUpdateImmutable(() -> {
+ mManager.updateShortcuts(list(makeShortcut("s1"), makeShortcut("ms2")));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.removeDynamicShortcuts(list("s1", "ms1"));
+ });
+ assertCannotUpdateImmutable(() -> {
+ mManager.removeDynamicShortcuts(list("s2", "ms2"));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.disableShortcuts(list("s1", "ms1"));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertCannotUpdateImmutable(() -> {
+ mManager.enableShortcuts(list("s1", "ms2"));
+ });
+ });
+ checkManifestShortcuts_immutable_verify();
+ }
}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index c23f9e6..7fa8a800 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -246,11 +246,22 @@
reset(o);
}
}
+
+ public static <T> List<T> assertEmpty(List<T> list) {
+ assertEquals(0, list.size());
+ return list;
+ }
+
public static void assertExpectException(Class<? extends Throwable> expectedExceptionType,
String expectedExceptionMessageRegex, Runnable r) {
assertExpectException("", expectedExceptionType, expectedExceptionMessageRegex, r);
}
+ public static void assertCannotUpdateImmutable(Runnable r) {
+ assertExpectException(
+ IllegalArgumentException.class, "may not be manipulated via APIs", r);
+ }
+
public static void assertDynamicShortcutCountExceeded(Runnable r) {
assertExpectException(IllegalArgumentException.class,
"Max number of dynamic shortcuts exceeded", r);
@@ -383,6 +394,46 @@
return actualShortcuts;
}
+ public static List<ShortcutInfo> assertAllManifest(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isManifestShortcut());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllNotManifest(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertFalse("ID " + s.getId(), s.isManifestShortcut());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllDisabled(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), !s.isEnabled());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllEnabled(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isEnabled());
+ }
+ return actualShortcuts;
+ }
+
+ public static List<ShortcutInfo> assertAllImmutable(
+ List<ShortcutInfo> actualShortcuts) {
+ for (ShortcutInfo s : actualShortcuts) {
+ assertTrue("ID " + s.getId(), s.isImmutable());
+ }
+ return actualShortcuts;
+ }
+
public static List<ShortcutInfo> assertAllStringsResolved(
List<ShortcutInfo> actualShortcuts) {
for (ShortcutInfo s : actualShortcuts) {
@@ -398,6 +449,7 @@
public static void assertPinnedOnly(ShortcutInfo si) {
assertFalse(si.isDynamic());
+ assertFalse(si.isManifestShortcut());
assertTrue(si.isPinned());
}