diff options
555 files changed, 15609 insertions, 7996 deletions
@@ -77,9 +77,9 @@ LOCAL_SRC_FILES += \ core/java/android/app/ISearchManagerCallback.aidl \ core/java/android/app/IServiceConnection.aidl \ core/java/android/app/IStopUserCallback.aidl \ - core/java/android/app/task/ITaskCallback.aidl \ - core/java/android/app/task/ITaskManager.aidl \ - core/java/android/app/task/ITaskService.aidl \ + core/java/android/app/job/IJobCallback.aidl \ + core/java/android/app/job/IJobScheduler.aidl \ + core/java/android/app/job/IJobService.aidl \ core/java/android/app/IThumbnailRetriever.aidl \ core/java/android/app/ITransientNotification.aidl \ core/java/android/app/IUiAutomationConnection.aidl \ diff --git a/CleanSpec.mk b/CleanSpec.mk index f3bb9b6..5b027b3 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -197,3 +197,6 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager) diff --git a/api/current.txt b/api/current.txt index ddb9397..d01edd4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -248,7 +248,7 @@ package android { field public static final int actionBarTabBarStyle = 16843508; // 0x10102f4 field public static final int actionBarTabStyle = 16843507; // 0x10102f3 field public static final int actionBarTabTextStyle = 16843509; // 0x10102f5 - field public static final int actionBarTheme = 16843828; // 0x1010434 + field public static final int actionBarTheme = 16843827; // 0x1010433 field public static final int actionBarWidgetTheme = 16843671; // 0x1010397 field public static final int actionButtonStyle = 16843480; // 0x10102d8 field public static final int actionDropDownStyle = 16843479; // 0x10102d7 @@ -265,7 +265,7 @@ package android { field public static final int actionModeSplitBackground = 16843677; // 0x101039d field public static final int actionModeStyle = 16843668; // 0x1010394 field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6 - field public static final int actionOverflowMenuStyle = 16843847; // 0x1010447 + field public static final int actionOverflowMenuStyle = 16843846; // 0x1010446 field public static final int actionProviderClass = 16843657; // 0x1010389 field public static final int actionViewClass = 16843516; // 0x10102fc field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd @@ -311,7 +311,7 @@ package android { field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b field public static final int autoLink = 16842928; // 0x10100b0 field public static final int autoMirrored = 16843754; // 0x10103ea - field public static final int autoRemoveFromRecents = 16843850; // 0x101044a + field public static final int autoRemoveFromRecents = 16843849; // 0x1010449 field public static final int autoStart = 16843445; // 0x10102b5 field public static final deprecated int autoText = 16843114; // 0x101016a field public static final int autoUrlDetect = 16843404; // 0x101028c @@ -384,22 +384,22 @@ package android { field public static final int codes = 16843330; // 0x1010242 field public static final int collapseColumns = 16843083; // 0x101014b field public static final int color = 16843173; // 0x10101a5 - field public static final int colorAccent = 16843832; // 0x1010438 + field public static final int colorAccent = 16843831; // 0x1010437 field public static final int colorActivatedHighlight = 16843664; // 0x1010390 field public static final int colorBackground = 16842801; // 0x1010031 field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab - field public static final int colorButtonNormal = 16843822; // 0x101042e - field public static final int colorControlActivated = 16843821; // 0x101042d - field public static final int colorControlHighlight = 16843823; // 0x101042f - field public static final int colorControlNormal = 16843820; // 0x101042c + field public static final int colorButtonNormal = 16843821; // 0x101042d + field public static final int colorControlActivated = 16843820; // 0x101042c + field public static final int colorControlHighlight = 16843822; // 0x101042e + field public static final int colorControlNormal = 16843819; // 0x101042b field public static final int colorFocusedHighlight = 16843663; // 0x101038f field public static final int colorForeground = 16842800; // 0x1010030 field public static final int colorForegroundInverse = 16843270; // 0x1010206 field public static final int colorLongPressedHighlight = 16843662; // 0x101038e field public static final int colorMultiSelectHighlight = 16843665; // 0x1010391 field public static final int colorPressedHighlight = 16843661; // 0x101038d - field public static final int colorPrimary = 16843830; // 0x1010436 - field public static final int colorPrimaryDark = 16843831; // 0x1010437 + field public static final int colorPrimary = 16843829; // 0x1010435 + field public static final int colorPrimaryDark = 16843830; // 0x1010436 field public static final int columnCount = 16843639; // 0x1010377 field public static final int columnDelay = 16843215; // 0x10101cf field public static final int columnOrderPreserved = 16843640; // 0x1010378 @@ -414,10 +414,10 @@ package android { field public static final int content = 16843355; // 0x101025b field public static final int contentAuthority = 16843408; // 0x1010290 field public static final int contentDescription = 16843379; // 0x1010273 - field public static final int contentInsetEnd = 16843863; // 0x1010457 - field public static final int contentInsetLeft = 16843864; // 0x1010458 - field public static final int contentInsetRight = 16843865; // 0x1010459 - field public static final int contentInsetStart = 16843862; // 0x1010456 + field public static final int contentInsetEnd = 16843862; // 0x1010456 + field public static final int contentInsetLeft = 16843863; // 0x1010457 + field public static final int contentInsetRight = 16843864; // 0x1010458 + field public static final int contentInsetStart = 16843861; // 0x1010455 field public static final int controlX1 = 16843798; // 0x1010416 field public static final int controlX2 = 16843800; // 0x1010418 field public static final int controlY1 = 16843799; // 0x1010417 @@ -462,7 +462,7 @@ package android { field public static final int dividerHorizontal = 16843564; // 0x101032c field public static final int dividerPadding = 16843562; // 0x101032a field public static final int dividerVertical = 16843530; // 0x101030a - field public static final int documentLaunchMode = 16843848; // 0x1010448 + field public static final int documentLaunchMode = 16843847; // 0x1010447 field public static final int drawSelectorOnTop = 16843004; // 0x10100fc field public static final int drawable = 16843161; // 0x1010199 field public static final int drawableBottom = 16843118; // 0x101016e @@ -491,8 +491,8 @@ package android { field public static final int editTextStyle = 16842862; // 0x101006e field public static final deprecated int editable = 16843115; // 0x101016b field public static final int editorExtras = 16843300; // 0x1010224 - field public static final int elegantTextHeight = 16843872; // 0x1010460 - field public static final int elevation = 16843843; // 0x1010443 + field public static final int elegantTextHeight = 16843871; // 0x101045f + field public static final int elevation = 16843842; // 0x1010442 field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 field public static final int enabled = 16842766; // 0x101000e @@ -502,10 +502,10 @@ package android { field public static final int entries = 16842930; // 0x10100b2 field public static final int entryValues = 16843256; // 0x10101f8 field public static final int eventsInterceptionEnabled = 16843389; // 0x101027d - field public static final int excludeClass = 16843845; // 0x1010445 + field public static final int excludeClass = 16843844; // 0x1010444 field public static final int excludeFromRecents = 16842775; // 0x1010017 - field public static final int excludeId = 16843844; // 0x1010444 - field public static final int excludeViewName = 16843857; // 0x1010451 + field public static final int excludeId = 16843843; // 0x1010443 + field public static final int excludeViewName = 16843856; // 0x1010450 field public static final int exitFadeDuration = 16843533; // 0x101030d field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052 field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053 @@ -567,7 +567,7 @@ package android { field public static final int freezesText = 16843116; // 0x101016c field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 - field public static final int fromId = 16843853; // 0x101044d + field public static final int fromId = 16843852; // 0x101044c field public static final int fromScene = 16843741; // 0x10103dd field public static final int fromXDelta = 16843206; // 0x10101c6 field public static final int fromXScale = 16843202; // 0x10101c2 @@ -600,7 +600,7 @@ package android { field public static final int headerBackground = 16843055; // 0x101012f field public static final int headerDividersEnabled = 16843310; // 0x101022e field public static final int height = 16843093; // 0x1010155 - field public static final int hideOnContentScroll = 16843846; // 0x1010446 + field public static final int hideOnContentScroll = 16843845; // 0x1010445 field public static final int hint = 16843088; // 0x1010150 field public static final int homeAsUpIndicator = 16843531; // 0x101030b field public static final int homeLayout = 16843549; // 0x101031d @@ -746,7 +746,7 @@ package android { field public static final int layout_centerVertical = 16843153; // 0x1010191 field public static final int layout_column = 16843084; // 0x101014c field public static final int layout_columnSpan = 16843645; // 0x101037d - field public static final int layout_columnWeight = 16843868; // 0x101045c + field public static final int layout_columnWeight = 16843867; // 0x101045b field public static final int layout_gravity = 16842931; // 0x10100b3 field public static final int layout_height = 16842997; // 0x10100f5 field public static final int layout_margin = 16842998; // 0x10100f6 @@ -758,7 +758,7 @@ package android { field public static final int layout_marginTop = 16843000; // 0x10100f8 field public static final int layout_row = 16843643; // 0x101037b field public static final int layout_rowSpan = 16843644; // 0x101037c - field public static final int layout_rowWeight = 16843867; // 0x101045b + field public static final int layout_rowWeight = 16843866; // 0x101045a field public static final int layout_scale = 16843155; // 0x1010193 field public static final int layout_span = 16843085; // 0x101014d field public static final int layout_toEndOf = 16843704; // 0x10103b8 @@ -797,7 +797,7 @@ package android { field public static final int manageSpaceActivity = 16842756; // 0x1010004 field public static final int mapViewStyle = 16842890; // 0x101008a field public static final int marqueeRepeatLimit = 16843293; // 0x101021d - field public static final int matchOrder = 16843858; // 0x1010452 + field public static final int matchOrder = 16843857; // 0x1010451 field public static final int max = 16843062; // 0x1010136 field public static final int maxDate = 16843584; // 0x1010340 field public static final int maxEms = 16843095; // 0x1010157 @@ -806,7 +806,7 @@ package android { field public static final int maxLength = 16843104; // 0x1010160 field public static final int maxLevel = 16843186; // 0x10101b2 field public static final int maxLines = 16843091; // 0x1010153 - field public static final int maxRecents = 16843849; // 0x1010449 + field public static final int maxRecents = 16843848; // 0x1010448 field public static final int maxRows = 16843059; // 0x1010133 field public static final int maxSdkVersion = 16843377; // 0x1010271 field public static final int maxWidth = 16843039; // 0x101011f @@ -831,10 +831,10 @@ package android { field public static final int moreIcon = 16843061; // 0x1010135 field public static final int multiprocess = 16842771; // 0x1010013 field public static final int name = 16842755; // 0x1010003 - field public static final int navigationBarColor = 16843861; // 0x1010455 + field public static final int navigationBarColor = 16843860; // 0x1010454 field public static final int navigationMode = 16843471; // 0x10102cf field public static final int negativeButtonText = 16843254; // 0x10101f6 - field public static final int nestedScrollingEnabled = 16843833; // 0x1010439 + field public static final int nestedScrollingEnabled = 16843832; // 0x1010438 field public static final int nextFocusDown = 16842980; // 0x10100e4 field public static final int nextFocusForward = 16843580; // 0x101033c field public static final int nextFocusLeft = 16842977; // 0x10100e1 @@ -859,13 +859,14 @@ package android { field public static final int overScrollFooter = 16843459; // 0x10102c3 field public static final int overScrollHeader = 16843458; // 0x10102c2 field public static final int overScrollMode = 16843457; // 0x10102c1 + field public static final int overlapAnchor = 16843876; // 0x1010464 field public static final int overridesImplicitlyEnabledSubtype = 16843682; // 0x10103a2 field public static final int packageNames = 16843649; // 0x1010381 field public static final int padding = 16842965; // 0x10100d5 field public static final int paddingBottom = 16842969; // 0x10100d9 field public static final int paddingEnd = 16843700; // 0x10103b4 field public static final int paddingLeft = 16842966; // 0x10100d6 - field public static final int paddingMode = 16843866; // 0x101045a + field public static final int paddingMode = 16843865; // 0x1010459 field public static final int paddingRight = 16842968; // 0x10100d8 field public static final int paddingStart = 16843699; // 0x10103b3 field public static final int paddingTop = 16842967; // 0x10100d7 @@ -884,11 +885,10 @@ package android { field public static final int permissionFlags = 16843719; // 0x10103c7 field public static final int permissionGroup = 16842762; // 0x101000a field public static final int permissionGroupFlags = 16843717; // 0x10103c5 - field public static final int persistable = 16843824; // 0x1010430 + field public static final int persistable = 16843823; // 0x101042f field public static final int persistent = 16842765; // 0x101000d field public static final int persistentDrawingCache = 16842990; // 0x10100ee field public static final deprecated int phoneNumber = 16843111; // 0x1010167 - field public static final int pinned = 16843819; // 0x101042b field public static final int pivotX = 16843189; // 0x10101b5 field public static final int pivotY = 16843190; // 0x10101b6 field public static final int popupAnimationStyle = 16843465; // 0x10102c9 @@ -961,7 +961,7 @@ package android { field public static final int restoreAnyVersion = 16843450; // 0x10102ba field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d field public static final int restrictedAccountType = 16843733; // 0x10103d5 - field public static final int reversible = 16843854; // 0x101044e + field public static final int reversible = 16843853; // 0x101044d field public static final int right = 16843183; // 0x10101af field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093 field public static final int ringtoneType = 16843257; // 0x10101f9 @@ -1001,6 +1001,9 @@ package android { field public static final int scrollbars = 16842974; // 0x10100de field public static final int scrollingCache = 16843006; // 0x10100fe field public static final deprecated int searchButtonText = 16843269; // 0x1010205 + field public static final int searchKeyphrase = 16843873; // 0x1010461 + field public static final int searchKeyphraseId = 16843872; // 0x1010460 + field public static final int searchKeyphraseSupportedLocales = 16843874; // 0x1010462 field public static final int searchMode = 16843221; // 0x10101d5 field public static final int searchSettingsDescription = 16843402; // 0x101028a field public static final int searchSuggestAuthority = 16843222; // 0x10101d6 @@ -1015,10 +1018,10 @@ package android { field public static final int selectAllOnFocus = 16843102; // 0x101015e field public static final int selectable = 16843238; // 0x10101e6 field public static final int selectableItemBackground = 16843534; // 0x101030e - field public static final int selectableItemBackgroundBorderless = 16843871; // 0x101045f + field public static final int selectableItemBackgroundBorderless = 16843870; // 0x101045e field public static final int selectedDateVerticalBar = 16843591; // 0x1010347 field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342 - field public static final int sessionService = 16843840; // 0x1010440 + field public static final int sessionService = 16843839; // 0x101043f field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int setupActivity = 16843766; // 0x10103f6 field public static final int shadowColor = 16843105; // 0x1010161 @@ -1040,7 +1043,7 @@ package android { field public static final int shrinkColumns = 16843082; // 0x101014a field public static final deprecated int singleLine = 16843101; // 0x101015d field public static final int singleUser = 16843711; // 0x10103bf - field public static final int slideEdge = 16843827; // 0x1010433 + field public static final int slideEdge = 16843826; // 0x1010432 field public static final int smallIcon = 16843422; // 0x101029e field public static final int smallScreens = 16843396; // 0x1010284 field public static final int smoothScrollbar = 16843313; // 0x1010231 @@ -1052,19 +1055,19 @@ package android { field public static final int spinnerStyle = 16842881; // 0x1010081 field public static final int spinnersShown = 16843595; // 0x101034b field public static final int splitMotionEvents = 16843503; // 0x10102ef - field public static final int splitTrack = 16843855; // 0x101044f + field public static final int splitTrack = 16843854; // 0x101044e field public static final int src = 16843033; // 0x1010119 field public static final int ssp = 16843747; // 0x10103e3 field public static final int sspPattern = 16843749; // 0x10103e5 field public static final int sspPrefix = 16843748; // 0x10103e4 field public static final int stackFromBottom = 16843005; // 0x10100fd - field public static final int stackViewStyle = 16843841; // 0x1010441 + field public static final int stackViewStyle = 16843840; // 0x1010440 field public static final int starStyle = 16842882; // 0x1010082 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 deprecated int startYear = 16843132; // 0x101017c - field public static final int stateListAnimator = 16843851; // 0x101044b + field public static final int stateListAnimator = 16843850; // 0x101044a field public static final int stateNotNeeded = 16842774; // 0x1010016 field public static final int state_above_anchor = 16842922; // 0x10100aa field public static final int state_accelerated = 16843547; // 0x101031b @@ -1089,7 +1092,7 @@ package android { field public static final int state_single = 16842915; // 0x10100a3 field public static final int state_window_focused = 16842909; // 0x101009d field public static final int staticWallpaperPreview = 16843569; // 0x1010331 - field public static final int statusBarColor = 16843860; // 0x1010454 + field public static final int statusBarColor = 16843859; // 0x1010453 field public static final int stepSize = 16843078; // 0x1010146 field public static final int stopWithTask = 16843626; // 0x101036a field public static final int streamType = 16843273; // 0x1010209 @@ -1101,7 +1104,7 @@ package android { field public static final int strokeOpacity = 16843810; // 0x1010422 field public static final int strokeWidth = 16843811; // 0x1010423 field public static final int subtitle = 16843473; // 0x10102d1 - field public static final int subtitleTextAppearance = 16843826; // 0x1010432 + field public static final int subtitleTextAppearance = 16843825; // 0x1010431 field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int subtypeExtraValue = 16843674; // 0x101039a field public static final int subtypeId = 16843713; // 0x10103c1 @@ -1118,7 +1121,7 @@ package android { field public static final int switchMinWidth = 16843632; // 0x1010370 field public static final int switchPadding = 16843633; // 0x1010371 field public static final int switchPreferenceStyle = 16843629; // 0x101036d - field public static final int switchStyle = 16843842; // 0x1010442 + field public static final int switchStyle = 16843841; // 0x1010441 field public static final int switchTextAppearance = 16843630; // 0x101036e field public static final int switchTextOff = 16843628; // 0x101036c field public static final int switchTextOn = 16843627; // 0x101036b @@ -1134,7 +1137,7 @@ package android { field public static final int targetId = 16843740; // 0x10103dc field public static final int targetPackage = 16842785; // 0x1010021 field public static final int targetSdkVersion = 16843376; // 0x1010270 - field public static final int targetViewName = 16843856; // 0x1010450 + field public static final int targetViewName = 16843855; // 0x101044f field public static final int taskAffinity = 16842770; // 0x1010012 field public static final int taskCloseEnterAnimation = 16842942; // 0x10100be field public static final int taskCloseExitAnimation = 16842943; // 0x10100bf @@ -1156,7 +1159,7 @@ package android { field public static final int textAppearanceLargeInverse = 16842819; // 0x1010043 field public static final int textAppearanceLargePopupMenu = 16843521; // 0x1010301 field public static final int textAppearanceListItem = 16843678; // 0x101039e - field public static final int textAppearanceListItemSecondary = 16843829; // 0x1010435 + field public static final int textAppearanceListItemSecondary = 16843828; // 0x1010434 field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f field public static final int textAppearanceMedium = 16842817; // 0x1010041 field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044 @@ -1220,11 +1223,11 @@ package android { field public static final int tintMode = 16843797; // 0x1010415 field public static final int title = 16843233; // 0x10101e1 field public static final int titleCondensed = 16843234; // 0x10101e2 - field public static final int titleTextAppearance = 16843825; // 0x1010431 + field public static final int titleTextAppearance = 16843824; // 0x1010430 field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 - field public static final int toId = 16843852; // 0x101044c + field public static final int toId = 16843851; // 0x101044b field public static final int toScene = 16843742; // 0x10103de field public static final int toXDelta = 16843207; // 0x10101c7 field public static final int toXScale = 16843203; // 0x10101c3 @@ -1243,8 +1246,8 @@ package android { field public static final int transition = 16843743; // 0x10103df field public static final int transitionGroup = 16843803; // 0x101041b field public static final int transitionOrdering = 16843744; // 0x10103e0 - field public static final int translateX = 16843869; // 0x101045d - field public static final int translateY = 16843870; // 0x101045e + field public static final int translateX = 16843868; // 0x101045c + field public static final int translateY = 16843869; // 0x101045d field public static final int translationX = 16843554; // 0x1010322 field public static final int translationY = 16843555; // 0x1010323 field public static final int translationZ = 16843796; // 0x1010414 @@ -1307,8 +1310,8 @@ package android { field public static final int windowActionBar = 16843469; // 0x10102cd field public static final int windowActionBarOverlay = 16843492; // 0x10102e4 field public static final int windowActionModeOverlay = 16843485; // 0x10102dd - field public static final int windowAllowEnterTransitionOverlap = 16843839; // 0x101043f - field public static final int windowAllowExitTransitionOverlap = 16843838; // 0x101043e + field public static final int windowAllowEnterTransitionOverlap = 16843838; // 0x101043e + field public static final int windowAllowExitTransitionOverlap = 16843837; // 0x101043d field public static final int windowAnimationStyle = 16842926; // 0x10100ae field public static final int windowBackground = 16842836; // 0x1010054 field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b @@ -1316,12 +1319,12 @@ package android { field public static final int windowContentTransitionManager = 16843795; // 0x1010413 field public static final int windowContentTransitions = 16843794; // 0x1010412 field public static final int windowDisablePreview = 16843298; // 0x1010222 - field public static final int windowDrawsSystemBarBackgrounds = 16843859; // 0x1010453 + field public static final int windowDrawsSystemBarBackgrounds = 16843858; // 0x1010452 field public static final int windowEnableSplitTouch = 16843543; // 0x1010317 field public static final int windowEnterAnimation = 16842932; // 0x10100b4 - field public static final int windowEnterTransition = 16843834; // 0x101043a + field public static final int windowEnterTransition = 16843833; // 0x1010439 field public static final int windowExitAnimation = 16842933; // 0x10100b5 - field public static final int windowExitTransition = 16843835; // 0x101043b + field public static final int windowExitTransition = 16843834; // 0x101043a field public static final int windowFrame = 16842837; // 0x1010055 field public static final int windowFullscreen = 16843277; // 0x101020d field public static final int windowHideAnimation = 16842935; // 0x10100b7 @@ -1332,8 +1335,8 @@ package android { field public static final int windowNoDisplay = 16843294; // 0x101021e field public static final int windowNoTitle = 16842838; // 0x1010056 field public static final int windowOverscan = 16843727; // 0x10103cf - field public static final int windowSharedElementEnterTransition = 16843836; // 0x101043c - field public static final int windowSharedElementExitTransition = 16843837; // 0x101043d + field public static final int windowSharedElementEnterTransition = 16843835; // 0x101043b + field public static final int windowSharedElementExitTransition = 16843836; // 0x101043c field public static final int windowShowAnimation = 16842934; // 0x10100b6 field public static final int windowShowWallpaper = 16843410; // 0x1010292 field public static final int windowSoftInputMode = 16843307; // 0x101022b @@ -1341,6 +1344,7 @@ package android { field public static final int windowTitleBackgroundStyle = 16842844; // 0x101005c field public static final int windowTitleSize = 16842842; // 0x101005a field public static final int windowTitleStyle = 16842843; // 0x101005b + field public static final int windowTransitionBackgroundFadeDuration = 16843875; // 0x1010463 field public static final int windowTranslucentNavigation = 16843760; // 0x10103f0 field public static final int windowTranslucentStatus = 16843759; // 0x10103ef field public static final int writePermission = 16842760; // 0x1010008 @@ -1865,52 +1869,52 @@ package android { field public static final int TextAppearance_Inverse = 16973887; // 0x103003f field public static final int TextAppearance_Large = 16973890; // 0x1030042 field public static final int TextAppearance_Large_Inverse = 16973891; // 0x1030043 - field public static final int TextAppearance_Material = 16974348; // 0x103020c - field public static final int TextAppearance_Material_Body1 = 16974546; // 0x10302d2 - field public static final int TextAppearance_Material_Body2 = 16974545; // 0x10302d1 - field public static final int TextAppearance_Material_Button = 16974549; // 0x10302d5 - field public static final int TextAppearance_Material_Caption = 16974547; // 0x10302d3 - field public static final int TextAppearance_Material_DialogWindowTitle = 16974349; // 0x103020d - field public static final int TextAppearance_Material_Display1 = 16974541; // 0x10302cd - field public static final int TextAppearance_Material_Display2 = 16974540; // 0x10302cc - field public static final int TextAppearance_Material_Display3 = 16974539; // 0x10302cb - field public static final int TextAppearance_Material_Display4 = 16974538; // 0x10302ca - field public static final int TextAppearance_Material_Headline = 16974542; // 0x10302ce - field public static final int TextAppearance_Material_Inverse = 16974350; // 0x103020e - field public static final int TextAppearance_Material_Large = 16974351; // 0x103020f - field public static final int TextAppearance_Material_Large_Inverse = 16974352; // 0x1030210 - field public static final int TextAppearance_Material_Medium = 16974353; // 0x1030211 - field public static final int TextAppearance_Material_Medium_Inverse = 16974354; // 0x1030212 - field public static final int TextAppearance_Material_Menu = 16974548; // 0x10302d4 - field public static final int TextAppearance_Material_SearchResult_Subtitle = 16974355; // 0x1030213 - field public static final int TextAppearance_Material_SearchResult_Title = 16974356; // 0x1030214 - field public static final int TextAppearance_Material_Small = 16974357; // 0x1030215 - field public static final int TextAppearance_Material_Small_Inverse = 16974358; // 0x1030216 - field public static final int TextAppearance_Material_Subhead = 16974544; // 0x10302d0 - field public static final int TextAppearance_Material_Title = 16974543; // 0x10302cf - field public static final int TextAppearance_Material_Widget = 16974360; // 0x1030218 - field public static final int TextAppearance_Material_Widget_ActionBar_Menu = 16974361; // 0x1030219 - field public static final int TextAppearance_Material_Widget_ActionBar_Subtitle = 16974362; // 0x103021a - field public static final int TextAppearance_Material_Widget_ActionBar_Subtitle_Inverse = 16974363; // 0x103021b - field public static final int TextAppearance_Material_Widget_ActionBar_Title = 16974364; // 0x103021c - field public static final int TextAppearance_Material_Widget_ActionBar_Title_Inverse = 16974365; // 0x103021d - field public static final int TextAppearance_Material_Widget_ActionMode_Subtitle = 16974366; // 0x103021e - field public static final int TextAppearance_Material_Widget_ActionMode_Subtitle_Inverse = 16974367; // 0x103021f - field public static final int TextAppearance_Material_Widget_ActionMode_Title = 16974368; // 0x1030220 - field public static final int TextAppearance_Material_Widget_ActionMode_Title_Inverse = 16974369; // 0x1030221 - field public static final int TextAppearance_Material_Widget_Button = 16974370; // 0x1030222 - field public static final int TextAppearance_Material_Widget_DropDownHint = 16974371; // 0x1030223 - field public static final int TextAppearance_Material_Widget_DropDownItem = 16974372; // 0x1030224 - field public static final int TextAppearance_Material_Widget_EditText = 16974373; // 0x1030225 - field public static final int TextAppearance_Material_Widget_IconMenu_Item = 16974374; // 0x1030226 - field public static final int TextAppearance_Material_Widget_PopupMenu = 16974375; // 0x1030227 - field public static final int TextAppearance_Material_Widget_PopupMenu_Large = 16974376; // 0x1030228 - field public static final int TextAppearance_Material_Widget_PopupMenu_Small = 16974377; // 0x1030229 - field public static final int TextAppearance_Material_Widget_TabWidget = 16974378; // 0x103022a - field public static final int TextAppearance_Material_Widget_TextView = 16974379; // 0x103022b - field public static final int TextAppearance_Material_Widget_TextView_PopupMenu = 16974380; // 0x103022c - field public static final int TextAppearance_Material_Widget_TextView_SpinnerItem = 16974381; // 0x103022d - field public static final int TextAppearance_Material_WindowTitle = 16974359; // 0x1030217 + field public static final int TextAppearance_Material = 16974350; // 0x103020e + field public static final int TextAppearance_Material_Body1 = 16974552; // 0x10302d8 + field public static final int TextAppearance_Material_Body2 = 16974551; // 0x10302d7 + field public static final int TextAppearance_Material_Button = 16974555; // 0x10302db + field public static final int TextAppearance_Material_Caption = 16974553; // 0x10302d9 + field public static final int TextAppearance_Material_DialogWindowTitle = 16974351; // 0x103020f + field public static final int TextAppearance_Material_Display1 = 16974547; // 0x10302d3 + field public static final int TextAppearance_Material_Display2 = 16974546; // 0x10302d2 + field public static final int TextAppearance_Material_Display3 = 16974545; // 0x10302d1 + field public static final int TextAppearance_Material_Display4 = 16974544; // 0x10302d0 + field public static final int TextAppearance_Material_Headline = 16974548; // 0x10302d4 + field public static final int TextAppearance_Material_Inverse = 16974352; // 0x1030210 + field public static final int TextAppearance_Material_Large = 16974353; // 0x1030211 + field public static final int TextAppearance_Material_Large_Inverse = 16974354; // 0x1030212 + field public static final int TextAppearance_Material_Medium = 16974355; // 0x1030213 + field public static final int TextAppearance_Material_Medium_Inverse = 16974356; // 0x1030214 + field public static final int TextAppearance_Material_Menu = 16974554; // 0x10302da + field public static final int TextAppearance_Material_SearchResult_Subtitle = 16974357; // 0x1030215 + field public static final int TextAppearance_Material_SearchResult_Title = 16974358; // 0x1030216 + field public static final int TextAppearance_Material_Small = 16974359; // 0x1030217 + field public static final int TextAppearance_Material_Small_Inverse = 16974360; // 0x1030218 + field public static final int TextAppearance_Material_Subhead = 16974550; // 0x10302d6 + field public static final int TextAppearance_Material_Title = 16974549; // 0x10302d5 + field public static final int TextAppearance_Material_Widget = 16974362; // 0x103021a + field public static final int TextAppearance_Material_Widget_ActionBar_Menu = 16974363; // 0x103021b + field public static final int TextAppearance_Material_Widget_ActionBar_Subtitle = 16974364; // 0x103021c + field public static final int TextAppearance_Material_Widget_ActionBar_Subtitle_Inverse = 16974365; // 0x103021d + field public static final int TextAppearance_Material_Widget_ActionBar_Title = 16974366; // 0x103021e + field public static final int TextAppearance_Material_Widget_ActionBar_Title_Inverse = 16974367; // 0x103021f + field public static final int TextAppearance_Material_Widget_ActionMode_Subtitle = 16974368; // 0x1030220 + field public static final int TextAppearance_Material_Widget_ActionMode_Subtitle_Inverse = 16974369; // 0x1030221 + field public static final int TextAppearance_Material_Widget_ActionMode_Title = 16974370; // 0x1030222 + field public static final int TextAppearance_Material_Widget_ActionMode_Title_Inverse = 16974371; // 0x1030223 + field public static final int TextAppearance_Material_Widget_Button = 16974372; // 0x1030224 + field public static final int TextAppearance_Material_Widget_DropDownHint = 16974373; // 0x1030225 + field public static final int TextAppearance_Material_Widget_DropDownItem = 16974374; // 0x1030226 + field public static final int TextAppearance_Material_Widget_EditText = 16974375; // 0x1030227 + field public static final int TextAppearance_Material_Widget_IconMenu_Item = 16974376; // 0x1030228 + field public static final int TextAppearance_Material_Widget_PopupMenu = 16974377; // 0x1030229 + field public static final int TextAppearance_Material_Widget_PopupMenu_Large = 16974378; // 0x103022a + field public static final int TextAppearance_Material_Widget_PopupMenu_Small = 16974379; // 0x103022b + field public static final int TextAppearance_Material_Widget_TabWidget = 16974380; // 0x103022c + field public static final int TextAppearance_Material_Widget_TextView = 16974381; // 0x103022d + field public static final int TextAppearance_Material_Widget_TextView_PopupMenu = 16974382; // 0x103022e + field public static final int TextAppearance_Material_Widget_TextView_SpinnerItem = 16974383; // 0x103022f + field public static final int TextAppearance_Material_WindowTitle = 16974361; // 0x1030219 field public static final int TextAppearance_Medium = 16973892; // 0x1030044 field public static final int TextAppearance_Medium_Inverse = 16973893; // 0x1030045 field public static final int TextAppearance_Small = 16973894; // 0x1030046 @@ -1936,12 +1940,12 @@ package android { field public static final int TextAppearance_Widget_TextView_SpinnerItem = 16973906; // 0x1030052 field public static final int TextAppearance_WindowTitle = 16973907; // 0x1030053 field public static final int Theme = 16973829; // 0x1030005 - field public static final int ThemeOverlay = 16974412; // 0x103024c - field public static final int ThemeOverlay_Material = 16974413; // 0x103024d - field public static final int ThemeOverlay_Material_ActionBar = 16974414; // 0x103024e - field public static final int ThemeOverlay_Material_Dark = 16974416; // 0x1030250 - field public static final int ThemeOverlay_Material_Dark_ActionBar = 16974417; // 0x1030251 - field public static final int ThemeOverlay_Material_Light = 16974415; // 0x103024f + field public static final int ThemeOverlay = 16974416; // 0x1030250 + field public static final int ThemeOverlay_Material = 16974417; // 0x1030251 + field public static final int ThemeOverlay_Material_ActionBar = 16974418; // 0x1030252 + field public static final int ThemeOverlay_Material_Dark = 16974420; // 0x1030254 + field public static final int ThemeOverlay_Material_Dark_ActionBar = 16974421; // 0x1030255 + field public static final int ThemeOverlay_Material_Light = 16974419; // 0x1030253 field public static final int Theme_Black = 16973832; // 0x1030008 field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009 field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a @@ -1971,6 +1975,7 @@ package android { field public static final int Theme_DeviceDefault_NoActionBar_Overscan = 16974303; // 0x10301df field public static final int Theme_DeviceDefault_NoActionBar_TranslucentDecor = 16974307; // 0x10301e3 field public static final int Theme_DeviceDefault_Panel = 16974138; // 0x103013a + field public static final int Theme_DeviceDefault_Settings = 16974384; // 0x1030230 field public static final int Theme_DeviceDefault_Wallpaper = 16974140; // 0x103013c field public static final int Theme_DeviceDefault_Wallpaper_NoTitleBar = 16974141; // 0x103013d field public static final int Theme_Dialog = 16973835; // 0x103000b @@ -2008,36 +2013,37 @@ package android { field public static final int Theme_Light_NoTitleBar_Fullscreen = 16973838; // 0x103000e field public static final int Theme_Light_Panel = 16973914; // 0x103005a field public static final int Theme_Light_WallpaperSettings = 16973922; // 0x1030062 - field public static final int Theme_Material = 16974382; // 0x103022e - field public static final int Theme_Material_Dialog = 16974383; // 0x103022f - field public static final int Theme_Material_DialogWhenLarge = 16974387; // 0x1030233 - field public static final int Theme_Material_DialogWhenLarge_NoActionBar = 16974388; // 0x1030234 - field public static final int Theme_Material_Dialog_MinWidth = 16974384; // 0x1030230 - field public static final int Theme_Material_Dialog_NoActionBar = 16974385; // 0x1030231 - field public static final int Theme_Material_Dialog_NoActionBar_MinWidth = 16974386; // 0x1030232 - field public static final int Theme_Material_InputMethod = 16974389; // 0x1030235 - field public static final int Theme_Material_Light = 16974398; // 0x103023e - field public static final int Theme_Material_Light_DarkActionBar = 16974399; // 0x103023f - field public static final int Theme_Material_Light_Dialog = 16974400; // 0x1030240 - field public static final int Theme_Material_Light_DialogWhenLarge = 16974404; // 0x1030244 - field public static final int Theme_Material_Light_DialogWhenLarge_NoActionBar = 16974405; // 0x1030245 - field public static final int Theme_Material_Light_Dialog_MinWidth = 16974401; // 0x1030241 - field public static final int Theme_Material_Light_Dialog_NoActionBar = 16974402; // 0x1030242 - field public static final int Theme_Material_Light_Dialog_NoActionBar_MinWidth = 16974403; // 0x1030243 - field public static final int Theme_Material_Light_NoActionBar = 16974406; // 0x1030246 - field public static final int Theme_Material_Light_NoActionBar_Fullscreen = 16974407; // 0x1030247 - field public static final int Theme_Material_Light_NoActionBar_Overscan = 16974408; // 0x1030248 - field public static final int Theme_Material_Light_NoActionBar_TranslucentDecor = 16974409; // 0x1030249 - field public static final int Theme_Material_Light_Panel = 16974410; // 0x103024a - field public static final int Theme_Material_Light_Voice = 16974411; // 0x103024b - field public static final int Theme_Material_NoActionBar = 16974390; // 0x1030236 - field public static final int Theme_Material_NoActionBar_Fullscreen = 16974391; // 0x1030237 - field public static final int Theme_Material_NoActionBar_Overscan = 16974392; // 0x1030238 - field public static final int Theme_Material_NoActionBar_TranslucentDecor = 16974393; // 0x1030239 - field public static final int Theme_Material_Panel = 16974394; // 0x103023a - field public static final int Theme_Material_Voice = 16974395; // 0x103023b - field public static final int Theme_Material_Wallpaper = 16974396; // 0x103023c - field public static final int Theme_Material_Wallpaper_NoTitleBar = 16974397; // 0x103023d + field public static final int Theme_Material = 16974385; // 0x1030231 + field public static final int Theme_Material_Dialog = 16974386; // 0x1030232 + field public static final int Theme_Material_DialogWhenLarge = 16974390; // 0x1030236 + field public static final int Theme_Material_DialogWhenLarge_NoActionBar = 16974391; // 0x1030237 + field public static final int Theme_Material_Dialog_MinWidth = 16974387; // 0x1030233 + field public static final int Theme_Material_Dialog_NoActionBar = 16974388; // 0x1030234 + field public static final int Theme_Material_Dialog_NoActionBar_MinWidth = 16974389; // 0x1030235 + field public static final int Theme_Material_InputMethod = 16974392; // 0x1030238 + field public static final int Theme_Material_Light = 16974402; // 0x1030242 + field public static final int Theme_Material_Light_DarkActionBar = 16974403; // 0x1030243 + field public static final int Theme_Material_Light_Dialog = 16974404; // 0x1030244 + field public static final int Theme_Material_Light_DialogWhenLarge = 16974408; // 0x1030248 + field public static final int Theme_Material_Light_DialogWhenLarge_NoActionBar = 16974409; // 0x1030249 + field public static final int Theme_Material_Light_Dialog_MinWidth = 16974405; // 0x1030245 + field public static final int Theme_Material_Light_Dialog_NoActionBar = 16974406; // 0x1030246 + field public static final int Theme_Material_Light_Dialog_NoActionBar_MinWidth = 16974407; // 0x1030247 + field public static final int Theme_Material_Light_NoActionBar = 16974410; // 0x103024a + field public static final int Theme_Material_Light_NoActionBar_Fullscreen = 16974411; // 0x103024b + field public static final int Theme_Material_Light_NoActionBar_Overscan = 16974412; // 0x103024c + field public static final int Theme_Material_Light_NoActionBar_TranslucentDecor = 16974413; // 0x103024d + field public static final int Theme_Material_Light_Panel = 16974414; // 0x103024e + field public static final int Theme_Material_Light_Voice = 16974415; // 0x103024f + field public static final int Theme_Material_NoActionBar = 16974393; // 0x1030239 + field public static final int Theme_Material_NoActionBar_Fullscreen = 16974394; // 0x103023a + field public static final int Theme_Material_NoActionBar_Overscan = 16974395; // 0x103023b + field public static final int Theme_Material_NoActionBar_TranslucentDecor = 16974396; // 0x103023c + field public static final int Theme_Material_Panel = 16974397; // 0x103023d + field public static final int Theme_Material_Settings = 16974398; // 0x103023e + field public static final int Theme_Material_Voice = 16974399; // 0x103023f + field public static final int Theme_Material_Wallpaper = 16974400; // 0x1030240 + field public static final int Theme_Material_Wallpaper_NoTitleBar = 16974401; // 0x1030241 field public static final int Theme_NoDisplay = 16973909; // 0x1030055 field public static final int Theme_NoTitleBar = 16973830; // 0x1030006 field public static final int Theme_NoTitleBar_Fullscreen = 16973831; // 0x1030007 @@ -2099,7 +2105,7 @@ package android { field public static final int Widget_DeviceDefault_DropDownItem_Spinner = 16974178; // 0x1030162 field public static final int Widget_DeviceDefault_EditText = 16974154; // 0x103014a field public static final int Widget_DeviceDefault_ExpandableListView = 16974155; // 0x103014b - field public static final int Widget_DeviceDefault_FastScroll = 16974344; // 0x1030208 + field public static final int Widget_DeviceDefault_FastScroll = 16974346; // 0x103020a field public static final int Widget_DeviceDefault_GridView = 16974156; // 0x103014c field public static final int Widget_DeviceDefault_HorizontalScrollView = 16974171; // 0x103015b field public static final int Widget_DeviceDefault_ImageButton = 16974157; // 0x103014d @@ -2133,7 +2139,7 @@ package android { field public static final int Widget_DeviceDefault_Light_DropDownItem_Spinner = 16974233; // 0x1030199 field public static final int Widget_DeviceDefault_Light_EditText = 16974206; // 0x103017e field public static final int Widget_DeviceDefault_Light_ExpandableListView = 16974207; // 0x103017f - field public static final int Widget_DeviceDefault_Light_FastScroll = 16974346; // 0x103020a + field public static final int Widget_DeviceDefault_Light_FastScroll = 16974348; // 0x103020c field public static final int Widget_DeviceDefault_Light_GridView = 16974208; // 0x1030180 field public static final int Widget_DeviceDefault_Light_HorizontalScrollView = 16974226; // 0x1030192 field public static final int Widget_DeviceDefault_Light_ImageButton = 16974209; // 0x1030181 @@ -2157,7 +2163,7 @@ package android { field public static final int Widget_DeviceDefault_Light_ScrollView = 16974225; // 0x1030191 field public static final int Widget_DeviceDefault_Light_SeekBar = 16974220; // 0x103018c field public static final int Widget_DeviceDefault_Light_Spinner = 16974227; // 0x1030193 - field public static final int Widget_DeviceDefault_Light_StackView = 16974347; // 0x103020b + field public static final int Widget_DeviceDefault_Light_StackView = 16974349; // 0x103020d field public static final int Widget_DeviceDefault_Light_Tab = 16974237; // 0x103019d field public static final int Widget_DeviceDefault_Light_TabWidget = 16974229; // 0x1030195 field public static final int Widget_DeviceDefault_Light_TextView = 16974202; // 0x103017a @@ -2181,7 +2187,7 @@ package android { field public static final int Widget_DeviceDefault_ScrollView = 16974170; // 0x103015a field public static final int Widget_DeviceDefault_SeekBar = 16974165; // 0x1030155 field public static final int Widget_DeviceDefault_Spinner = 16974172; // 0x103015c - field public static final int Widget_DeviceDefault_StackView = 16974345; // 0x1030209 + field public static final int Widget_DeviceDefault_StackView = 16974347; // 0x103020b field public static final int Widget_DeviceDefault_Tab = 16974189; // 0x103016d field public static final int Widget_DeviceDefault_TabWidget = 16974174; // 0x103015e field public static final int Widget_DeviceDefault_TextView = 16974150; // 0x1030146 @@ -2224,7 +2230,7 @@ package android { field public static final int Widget_Holo_DropDownItem_Spinner = 16973995; // 0x10300ab field public static final int Widget_Holo_EditText = 16973971; // 0x1030093 field public static final int Widget_Holo_ExpandableListView = 16973972; // 0x1030094 - field public static final int Widget_Holo_FastScroll = 16974339; // 0x1030203 + field public static final int Widget_Holo_FastScroll = 16974341; // 0x1030205 field public static final int Widget_Holo_GridView = 16973973; // 0x1030095 field public static final int Widget_Holo_HorizontalScrollView = 16973988; // 0x10300a4 field public static final int Widget_Holo_ImageButton = 16973974; // 0x1030096 @@ -2245,7 +2251,7 @@ package android { field public static final int Widget_Holo_Light_ActionMode_Inverse = 16974119; // 0x1030127 field public static final int Widget_Holo_Light_AutoCompleteTextView = 16974011; // 0x10300bb field public static final int Widget_Holo_Light_Button = 16974006; // 0x10300b6 - field public static final int Widget_Holo_Light_Button_Borderless = 16974341; // 0x1030205 + field public static final int Widget_Holo_Light_Button_Borderless = 16974343; // 0x1030207 field public static final int Widget_Holo_Light_Button_Borderless_Small = 16974107; // 0x103011b field public static final int Widget_Holo_Light_Button_Inset = 16974008; // 0x10300b8 field public static final int Widget_Holo_Light_Button_Small = 16974007; // 0x10300b7 @@ -2259,7 +2265,7 @@ package android { field public static final int Widget_Holo_Light_DropDownItem_Spinner = 16974041; // 0x10300d9 field public static final int Widget_Holo_Light_EditText = 16974014; // 0x10300be field public static final int Widget_Holo_Light_ExpandableListView = 16974015; // 0x10300bf - field public static final int Widget_Holo_Light_FastScroll = 16974342; // 0x1030206 + field public static final int Widget_Holo_Light_FastScroll = 16974344; // 0x1030208 field public static final int Widget_Holo_Light_GridView = 16974016; // 0x10300c0 field public static final int Widget_Holo_Light_HorizontalScrollView = 16974034; // 0x10300d2 field public static final int Widget_Holo_Light_ImageButton = 16974017; // 0x10300c1 @@ -2283,7 +2289,7 @@ package android { field public static final int Widget_Holo_Light_ScrollView = 16974033; // 0x10300d1 field public static final int Widget_Holo_Light_SeekBar = 16974028; // 0x10300cc field public static final int Widget_Holo_Light_Spinner = 16974035; // 0x10300d3 - field public static final int Widget_Holo_Light_StackView = 16974343; // 0x1030207 + field public static final int Widget_Holo_Light_StackView = 16974345; // 0x1030209 field public static final int Widget_Holo_Light_Tab = 16974052; // 0x10300e4 field public static final int Widget_Holo_Light_TabWidget = 16974037; // 0x10300d5 field public static final int Widget_Holo_Light_TextView = 16974010; // 0x10300ba @@ -2307,7 +2313,7 @@ package android { field public static final int Widget_Holo_ScrollView = 16973987; // 0x10300a3 field public static final int Widget_Holo_SeekBar = 16973982; // 0x103009e field public static final int Widget_Holo_Spinner = 16973989; // 0x10300a5 - field public static final int Widget_Holo_StackView = 16974340; // 0x1030204 + field public static final int Widget_Holo_StackView = 16974342; // 0x1030206 field public static final int Widget_Holo_Tab = 16974051; // 0x10300e3 field public static final int Widget_Holo_TabWidget = 16973991; // 0x10300a7 field public static final int Widget_Holo_TextView = 16973967; // 0x103008f @@ -2322,126 +2328,128 @@ package android { field public static final int Widget_ListView_DropDown = 16973872; // 0x1030030 field public static final int Widget_ListView_Menu = 16973873; // 0x1030031 field public static final int Widget_ListView_White = 16973871; // 0x103002f - field public static final int Widget_Material = 16974418; // 0x1030252 - field public static final int Widget_Material_ActionBar = 16974419; // 0x1030253 - field public static final int Widget_Material_ActionBar_Solid = 16974420; // 0x1030254 - field public static final int Widget_Material_ActionBar_TabBar = 16974421; // 0x1030255 - field public static final int Widget_Material_ActionBar_TabText = 16974422; // 0x1030256 - field public static final int Widget_Material_ActionBar_TabView = 16974423; // 0x1030257 - field public static final int Widget_Material_ActionButton = 16974424; // 0x1030258 - field public static final int Widget_Material_ActionButton_CloseMode = 16974425; // 0x1030259 - field public static final int Widget_Material_ActionButton_Overflow = 16974426; // 0x103025a - field public static final int Widget_Material_ActionMode = 16974427; // 0x103025b - field public static final int Widget_Material_AutoCompleteTextView = 16974428; // 0x103025c - field public static final int Widget_Material_Button = 16974429; // 0x103025d - field public static final int Widget_Material_ButtonBar = 16974435; // 0x1030263 - field public static final int Widget_Material_ButtonBar_AlertDialog = 16974436; // 0x1030264 - field public static final int Widget_Material_Button_Borderless = 16974430; // 0x103025e - field public static final int Widget_Material_Button_Borderless_Small = 16974431; // 0x103025f - field public static final int Widget_Material_Button_Inset = 16974432; // 0x1030260 - field public static final int Widget_Material_Button_Small = 16974433; // 0x1030261 - field public static final int Widget_Material_Button_Toggle = 16974434; // 0x1030262 - field public static final int Widget_Material_CalendarView = 16974437; // 0x1030265 - field public static final int Widget_Material_CheckedTextView = 16974438; // 0x1030266 - field public static final int Widget_Material_CompoundButton_CheckBox = 16974439; // 0x1030267 - field public static final int Widget_Material_CompoundButton_RadioButton = 16974440; // 0x1030268 - field public static final int Widget_Material_CompoundButton_Star = 16974441; // 0x1030269 - field public static final int Widget_Material_DatePicker = 16974442; // 0x103026a - field public static final int Widget_Material_DropDownItem = 16974443; // 0x103026b - field public static final int Widget_Material_DropDownItem_Spinner = 16974444; // 0x103026c - field public static final int Widget_Material_EditText = 16974445; // 0x103026d - field public static final int Widget_Material_ExpandableListView = 16974446; // 0x103026e - field public static final int Widget_Material_FastScroll = 16974447; // 0x103026f - field public static final int Widget_Material_GridView = 16974448; // 0x1030270 - field public static final int Widget_Material_HorizontalScrollView = 16974449; // 0x1030271 - field public static final int Widget_Material_ImageButton = 16974450; // 0x1030272 - field public static final int Widget_Material_Light = 16974477; // 0x103028d - field public static final int Widget_Material_Light_ActionBar = 16974478; // 0x103028e - field public static final int Widget_Material_Light_ActionBar_Solid = 16974479; // 0x103028f - field public static final int Widget_Material_Light_ActionBar_TabBar = 16974480; // 0x1030290 - field public static final int Widget_Material_Light_ActionBar_TabText = 16974481; // 0x1030291 - field public static final int Widget_Material_Light_ActionBar_TabView = 16974482; // 0x1030292 - field public static final int Widget_Material_Light_ActionButton = 16974483; // 0x1030293 - field public static final int Widget_Material_Light_ActionButton_CloseMode = 16974484; // 0x1030294 - field public static final int Widget_Material_Light_ActionButton_Overflow = 16974485; // 0x1030295 - field public static final int Widget_Material_Light_ActionMode = 16974486; // 0x1030296 - field public static final int Widget_Material_Light_AutoCompleteTextView = 16974487; // 0x1030297 - field public static final int Widget_Material_Light_Button = 16974488; // 0x1030298 - field public static final int Widget_Material_Light_ButtonBar = 16974494; // 0x103029e - field public static final int Widget_Material_Light_ButtonBar_AlertDialog = 16974495; // 0x103029f - field public static final int Widget_Material_Light_Button_Borderless = 16974489; // 0x1030299 - field public static final int Widget_Material_Light_Button_Borderless_Small = 16974490; // 0x103029a - field public static final int Widget_Material_Light_Button_Inset = 16974491; // 0x103029b - field public static final int Widget_Material_Light_Button_Small = 16974492; // 0x103029c - field public static final int Widget_Material_Light_Button_Toggle = 16974493; // 0x103029d - field public static final int Widget_Material_Light_CalendarView = 16974496; // 0x10302a0 - field public static final int Widget_Material_Light_CheckedTextView = 16974497; // 0x10302a1 - field public static final int Widget_Material_Light_CompoundButton_CheckBox = 16974498; // 0x10302a2 - field public static final int Widget_Material_Light_CompoundButton_RadioButton = 16974499; // 0x10302a3 - field public static final int Widget_Material_Light_CompoundButton_Star = 16974500; // 0x10302a4 - field public static final int Widget_Material_Light_DropDownItem = 16974501; // 0x10302a5 - field public static final int Widget_Material_Light_DropDownItem_Spinner = 16974502; // 0x10302a6 - field public static final int Widget_Material_Light_EditText = 16974503; // 0x10302a7 - field public static final int Widget_Material_Light_ExpandableListView = 16974504; // 0x10302a8 - field public static final int Widget_Material_Light_FastScroll = 16974505; // 0x10302a9 - field public static final int Widget_Material_Light_GridView = 16974506; // 0x10302aa - field public static final int Widget_Material_Light_HorizontalScrollView = 16974507; // 0x10302ab - field public static final int Widget_Material_Light_ImageButton = 16974508; // 0x10302ac - field public static final int Widget_Material_Light_ListPopupWindow = 16974509; // 0x10302ad - field public static final int Widget_Material_Light_ListView = 16974510; // 0x10302ae - field public static final int Widget_Material_Light_ListView_DropDown = 16974511; // 0x10302af - field public static final int Widget_Material_Light_MediaRouteButton = 16974512; // 0x10302b0 - field public static final int Widget_Material_Light_PopupMenu = 16974513; // 0x10302b1 - field public static final int Widget_Material_Light_PopupMenu_Overflow = 16974514; // 0x10302b2 - field public static final int Widget_Material_Light_PopupWindow = 16974515; // 0x10302b3 - field public static final int Widget_Material_Light_ProgressBar = 16974516; // 0x10302b4 - field public static final int Widget_Material_Light_ProgressBar_Horizontal = 16974517; // 0x10302b5 - field public static final int Widget_Material_Light_ProgressBar_Inverse = 16974518; // 0x10302b6 - field public static final int Widget_Material_Light_ProgressBar_Large = 16974519; // 0x10302b7 - field public static final int Widget_Material_Light_ProgressBar_Large_Inverse = 16974520; // 0x10302b8 - field public static final int Widget_Material_Light_ProgressBar_Small = 16974521; // 0x10302b9 - field public static final int Widget_Material_Light_ProgressBar_Small_Inverse = 16974522; // 0x10302ba - field public static final int Widget_Material_Light_ProgressBar_Small_Title = 16974523; // 0x10302bb - field public static final int Widget_Material_Light_RatingBar = 16974524; // 0x10302bc - field public static final int Widget_Material_Light_RatingBar_Indicator = 16974525; // 0x10302bd - field public static final int Widget_Material_Light_RatingBar_Small = 16974526; // 0x10302be - field public static final int Widget_Material_Light_ScrollView = 16974527; // 0x10302bf - field public static final int Widget_Material_Light_SeekBar = 16974528; // 0x10302c0 - field public static final int Widget_Material_Light_SegmentedButton = 16974529; // 0x10302c1 - field public static final int Widget_Material_Light_Spinner = 16974531; // 0x10302c3 - field public static final int Widget_Material_Light_StackView = 16974530; // 0x10302c2 - field public static final int Widget_Material_Light_Tab = 16974532; // 0x10302c4 - field public static final int Widget_Material_Light_TabWidget = 16974533; // 0x10302c5 - field public static final int Widget_Material_Light_TextView = 16974534; // 0x10302c6 - field public static final int Widget_Material_Light_TextView_SpinnerItem = 16974535; // 0x10302c7 - field public static final int Widget_Material_Light_WebTextView = 16974536; // 0x10302c8 - field public static final int Widget_Material_Light_WebView = 16974537; // 0x10302c9 - field public static final int Widget_Material_ListPopupWindow = 16974451; // 0x1030273 - field public static final int Widget_Material_ListView = 16974452; // 0x1030274 - field public static final int Widget_Material_ListView_DropDown = 16974453; // 0x1030275 - field public static final int Widget_Material_MediaRouteButton = 16974454; // 0x1030276 - field public static final int Widget_Material_PopupMenu = 16974455; // 0x1030277 - field public static final int Widget_Material_PopupMenu_Overflow = 16974456; // 0x1030278 - field public static final int Widget_Material_PopupWindow = 16974457; // 0x1030279 - field public static final int Widget_Material_ProgressBar = 16974458; // 0x103027a - field public static final int Widget_Material_ProgressBar_Horizontal = 16974459; // 0x103027b - field public static final int Widget_Material_ProgressBar_Large = 16974460; // 0x103027c - field public static final int Widget_Material_ProgressBar_Small = 16974461; // 0x103027d - field public static final int Widget_Material_ProgressBar_Small_Title = 16974462; // 0x103027e - field public static final int Widget_Material_RatingBar = 16974463; // 0x103027f - field public static final int Widget_Material_RatingBar_Indicator = 16974464; // 0x1030280 - field public static final int Widget_Material_RatingBar_Small = 16974465; // 0x1030281 - field public static final int Widget_Material_ScrollView = 16974466; // 0x1030282 - field public static final int Widget_Material_SeekBar = 16974467; // 0x1030283 - field public static final int Widget_Material_SegmentedButton = 16974468; // 0x1030284 - field public static final int Widget_Material_Spinner = 16974470; // 0x1030286 - field public static final int Widget_Material_StackView = 16974469; // 0x1030285 - field public static final int Widget_Material_Tab = 16974471; // 0x1030287 - field public static final int Widget_Material_TabWidget = 16974472; // 0x1030288 - field public static final int Widget_Material_TextView = 16974473; // 0x1030289 - field public static final int Widget_Material_TextView_SpinnerItem = 16974474; // 0x103028a - field public static final int Widget_Material_WebTextView = 16974475; // 0x103028b - field public static final int Widget_Material_WebView = 16974476; // 0x103028c + field public static final int Widget_Material = 16974422; // 0x1030256 + field public static final int Widget_Material_ActionBar = 16974423; // 0x1030257 + field public static final int Widget_Material_ActionBar_Solid = 16974424; // 0x1030258 + field public static final int Widget_Material_ActionBar_TabBar = 16974425; // 0x1030259 + field public static final int Widget_Material_ActionBar_TabText = 16974426; // 0x103025a + field public static final int Widget_Material_ActionBar_TabView = 16974427; // 0x103025b + field public static final int Widget_Material_ActionButton = 16974428; // 0x103025c + field public static final int Widget_Material_ActionButton_CloseMode = 16974429; // 0x103025d + field public static final int Widget_Material_ActionButton_Overflow = 16974430; // 0x103025e + field public static final int Widget_Material_ActionMode = 16974431; // 0x103025f + field public static final int Widget_Material_AutoCompleteTextView = 16974432; // 0x1030260 + field public static final int Widget_Material_Button = 16974433; // 0x1030261 + field public static final int Widget_Material_ButtonBar = 16974439; // 0x1030267 + field public static final int Widget_Material_ButtonBar_AlertDialog = 16974440; // 0x1030268 + field public static final int Widget_Material_Button_Borderless = 16974434; // 0x1030262 + field public static final int Widget_Material_Button_Borderless_Small = 16974435; // 0x1030263 + field public static final int Widget_Material_Button_Inset = 16974436; // 0x1030264 + field public static final int Widget_Material_Button_Small = 16974437; // 0x1030265 + field public static final int Widget_Material_Button_Toggle = 16974438; // 0x1030266 + field public static final int Widget_Material_CalendarView = 16974441; // 0x1030269 + field public static final int Widget_Material_CheckedTextView = 16974442; // 0x103026a + field public static final int Widget_Material_CompoundButton_CheckBox = 16974443; // 0x103026b + field public static final int Widget_Material_CompoundButton_RadioButton = 16974444; // 0x103026c + field public static final int Widget_Material_CompoundButton_Star = 16974445; // 0x103026d + field public static final int Widget_Material_DatePicker = 16974446; // 0x103026e + field public static final int Widget_Material_DropDownItem = 16974447; // 0x103026f + field public static final int Widget_Material_DropDownItem_Spinner = 16974448; // 0x1030270 + field public static final int Widget_Material_EditText = 16974449; // 0x1030271 + field public static final int Widget_Material_ExpandableListView = 16974450; // 0x1030272 + field public static final int Widget_Material_FastScroll = 16974451; // 0x1030273 + field public static final int Widget_Material_GridView = 16974452; // 0x1030274 + field public static final int Widget_Material_HorizontalScrollView = 16974453; // 0x1030275 + field public static final int Widget_Material_ImageButton = 16974454; // 0x1030276 + field public static final int Widget_Material_Light = 16974483; // 0x1030293 + field public static final int Widget_Material_Light_ActionBar = 16974484; // 0x1030294 + field public static final int Widget_Material_Light_ActionBar_Solid = 16974485; // 0x1030295 + field public static final int Widget_Material_Light_ActionBar_TabBar = 16974486; // 0x1030296 + field public static final int Widget_Material_Light_ActionBar_TabText = 16974487; // 0x1030297 + field public static final int Widget_Material_Light_ActionBar_TabView = 16974488; // 0x1030298 + field public static final int Widget_Material_Light_ActionButton = 16974489; // 0x1030299 + field public static final int Widget_Material_Light_ActionButton_CloseMode = 16974490; // 0x103029a + field public static final int Widget_Material_Light_ActionButton_Overflow = 16974491; // 0x103029b + field public static final int Widget_Material_Light_ActionMode = 16974492; // 0x103029c + field public static final int Widget_Material_Light_AutoCompleteTextView = 16974493; // 0x103029d + field public static final int Widget_Material_Light_Button = 16974494; // 0x103029e + field public static final int Widget_Material_Light_ButtonBar = 16974500; // 0x10302a4 + field public static final int Widget_Material_Light_ButtonBar_AlertDialog = 16974501; // 0x10302a5 + field public static final int Widget_Material_Light_Button_Borderless = 16974495; // 0x103029f + field public static final int Widget_Material_Light_Button_Borderless_Small = 16974496; // 0x10302a0 + field public static final int Widget_Material_Light_Button_Inset = 16974497; // 0x10302a1 + field public static final int Widget_Material_Light_Button_Small = 16974498; // 0x10302a2 + field public static final int Widget_Material_Light_Button_Toggle = 16974499; // 0x10302a3 + field public static final int Widget_Material_Light_CalendarView = 16974502; // 0x10302a6 + field public static final int Widget_Material_Light_CheckedTextView = 16974503; // 0x10302a7 + field public static final int Widget_Material_Light_CompoundButton_CheckBox = 16974504; // 0x10302a8 + field public static final int Widget_Material_Light_CompoundButton_RadioButton = 16974505; // 0x10302a9 + field public static final int Widget_Material_Light_CompoundButton_Star = 16974506; // 0x10302aa + field public static final int Widget_Material_Light_DropDownItem = 16974507; // 0x10302ab + field public static final int Widget_Material_Light_DropDownItem_Spinner = 16974508; // 0x10302ac + field public static final int Widget_Material_Light_EditText = 16974509; // 0x10302ad + field public static final int Widget_Material_Light_ExpandableListView = 16974510; // 0x10302ae + field public static final int Widget_Material_Light_FastScroll = 16974511; // 0x10302af + field public static final int Widget_Material_Light_GridView = 16974512; // 0x10302b0 + field public static final int Widget_Material_Light_HorizontalScrollView = 16974513; // 0x10302b1 + field public static final int Widget_Material_Light_ImageButton = 16974514; // 0x10302b2 + field public static final int Widget_Material_Light_ListPopupWindow = 16974515; // 0x10302b3 + field public static final int Widget_Material_Light_ListView = 16974516; // 0x10302b4 + field public static final int Widget_Material_Light_ListView_DropDown = 16974517; // 0x10302b5 + field public static final int Widget_Material_Light_MediaRouteButton = 16974518; // 0x10302b6 + field public static final int Widget_Material_Light_PopupMenu = 16974519; // 0x10302b7 + field public static final int Widget_Material_Light_PopupMenu_Overflow = 16974520; // 0x10302b8 + field public static final int Widget_Material_Light_PopupWindow = 16974521; // 0x10302b9 + field public static final int Widget_Material_Light_ProgressBar = 16974522; // 0x10302ba + field public static final int Widget_Material_Light_ProgressBar_Horizontal = 16974523; // 0x10302bb + field public static final int Widget_Material_Light_ProgressBar_Inverse = 16974524; // 0x10302bc + field public static final int Widget_Material_Light_ProgressBar_Large = 16974525; // 0x10302bd + field public static final int Widget_Material_Light_ProgressBar_Large_Inverse = 16974526; // 0x10302be + field public static final int Widget_Material_Light_ProgressBar_Small = 16974527; // 0x10302bf + field public static final int Widget_Material_Light_ProgressBar_Small_Inverse = 16974528; // 0x10302c0 + field public static final int Widget_Material_Light_ProgressBar_Small_Title = 16974529; // 0x10302c1 + field public static final int Widget_Material_Light_RatingBar = 16974530; // 0x10302c2 + field public static final int Widget_Material_Light_RatingBar_Indicator = 16974531; // 0x10302c3 + field public static final int Widget_Material_Light_RatingBar_Small = 16974532; // 0x10302c4 + field public static final int Widget_Material_Light_ScrollView = 16974533; // 0x10302c5 + field public static final int Widget_Material_Light_SeekBar = 16974534; // 0x10302c6 + field public static final int Widget_Material_Light_SegmentedButton = 16974535; // 0x10302c7 + field public static final int Widget_Material_Light_Spinner = 16974537; // 0x10302c9 + field public static final int Widget_Material_Light_StackView = 16974536; // 0x10302c8 + field public static final int Widget_Material_Light_Tab = 16974538; // 0x10302ca + field public static final int Widget_Material_Light_TabWidget = 16974539; // 0x10302cb + field public static final int Widget_Material_Light_TextView = 16974540; // 0x10302cc + field public static final int Widget_Material_Light_TextView_SpinnerItem = 16974541; // 0x10302cd + field public static final int Widget_Material_Light_WebTextView = 16974542; // 0x10302ce + field public static final int Widget_Material_Light_WebView = 16974543; // 0x10302cf + field public static final int Widget_Material_ListPopupWindow = 16974455; // 0x1030277 + field public static final int Widget_Material_ListView = 16974456; // 0x1030278 + field public static final int Widget_Material_ListView_DropDown = 16974457; // 0x1030279 + field public static final int Widget_Material_MediaRouteButton = 16974458; // 0x103027a + field public static final int Widget_Material_PopupMenu = 16974459; // 0x103027b + field public static final int Widget_Material_PopupMenu_Overflow = 16974460; // 0x103027c + field public static final int Widget_Material_PopupWindow = 16974461; // 0x103027d + field public static final int Widget_Material_ProgressBar = 16974462; // 0x103027e + field public static final int Widget_Material_ProgressBar_Horizontal = 16974463; // 0x103027f + field public static final int Widget_Material_ProgressBar_Large = 16974464; // 0x1030280 + field public static final int Widget_Material_ProgressBar_Small = 16974465; // 0x1030281 + field public static final int Widget_Material_ProgressBar_Small_Title = 16974466; // 0x1030282 + field public static final int Widget_Material_RatingBar = 16974467; // 0x1030283 + field public static final int Widget_Material_RatingBar_Indicator = 16974468; // 0x1030284 + field public static final int Widget_Material_RatingBar_Small = 16974469; // 0x1030285 + field public static final int Widget_Material_ScrollView = 16974470; // 0x1030286 + field public static final int Widget_Material_SeekBar = 16974471; // 0x1030287 + field public static final int Widget_Material_SegmentedButton = 16974472; // 0x1030288 + field public static final int Widget_Material_Spinner = 16974474; // 0x103028a + field public static final int Widget_Material_StackView = 16974473; // 0x1030289 + field public static final int Widget_Material_Tab = 16974475; // 0x103028b + field public static final int Widget_Material_TabWidget = 16974476; // 0x103028c + field public static final int Widget_Material_TextView = 16974477; // 0x103028d + field public static final int Widget_Material_TextView_SpinnerItem = 16974478; // 0x103028e + field public static final int Widget_Material_Toolbar = 16974479; // 0x103028f + field public static final int Widget_Material_Toolbar_Button_Navigation = 16974480; // 0x1030290 + field public static final int Widget_Material_WebTextView = 16974481; // 0x1030291 + field public static final int Widget_Material_WebView = 16974482; // 0x1030292 field public static final int Widget_PopupMenu = 16973958; // 0x1030086 field public static final int Widget_PopupWindow = 16973878; // 0x1030036 field public static final int Widget_ProgressBar = 16973852; // 0x103001c @@ -2461,6 +2469,8 @@ package android { field public static final int Widget_TextView = 16973858; // 0x1030022 field public static final int Widget_TextView_PopupMenu = 16973865; // 0x1030029 field public static final int Widget_TextView_SpinnerItem = 16973866; // 0x103002a + field public static final int Widget_Toolbar = 16974339; // 0x1030203 + field public static final int Widget_Toolbar_Button_Navigation = 16974340; // 0x1030204 field public static final int Widget_WebView = 16973875; // 0x1030033 field public static final int l_resource_pad1 = 16974336; // 0x1030200 field public static final int l_resource_pad10 = 16974327; // 0x10301f7 @@ -3339,6 +3349,7 @@ package android.app { method public void openContextMenu(android.view.View); method public void openOptionsMenu(); method public void overridePendingTransition(int, int); + method public void postponeEnterTransition(); method public void recreate(); method public void registerForContextMenu(android.view.View); method public final deprecated void removeDialog(int); @@ -3394,6 +3405,7 @@ package android.app { method public deprecated void startManagingCursor(android.database.Cursor); method public boolean startNextMatchingActivity(android.content.Intent); method public boolean startNextMatchingActivity(android.content.Intent, android.os.Bundle); + method public void startPostponedEnterTransition(); method public void startSearch(java.lang.String, boolean, android.os.Bundle, boolean); method public void stopLockTask(); method public deprecated void stopManagingCursor(android.database.Cursor); @@ -5188,6 +5200,7 @@ package android.app.admin { method public void clearDeviceOwnerApp(); method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String); method public void clearUserRestriction(android.content.ComponentName, java.lang.String); + method public android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle); method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String); method public java.lang.String[] getAccountTypesWithManagementDisabled(); method public java.util.List<android.content.ComponentName> getActiveAdmins(); @@ -5369,23 +5382,9 @@ package android.app.backup { } -package android.app.maintenance { +package android.app.job { - public abstract class IdleService extends android.app.Service { - ctor public IdleService(); - method public final void finishIdle(); - method public final android.os.IBinder onBind(android.content.Intent); - method public abstract boolean onIdleStart(); - method public abstract void onIdleStop(); - field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_IDLE_SERVICE"; - field public static final java.lang.String SERVICE_INTERFACE = "android.service.idle.IdleService"; - } - -} - -package android.app.task { - - public class Task implements android.os.Parcelable { + public class JobInfo implements android.os.Parcelable { method public int describeContents(); method public int getBackoffPolicy(); method public android.os.PersistableBundle getExtras(); @@ -5403,55 +5402,55 @@ package android.app.task { field public static final android.os.Parcelable.Creator CREATOR; } - public static abstract interface Task.BackoffPolicy { + public static abstract interface JobInfo.BackoffPolicy { field public static final int EXPONENTIAL = 1; // 0x1 field public static final int LINEAR = 0; // 0x0 } - public static final class Task.Builder { - ctor public Task.Builder(int, android.content.ComponentName); - method public android.app.task.Task build(); - method public android.app.task.Task.Builder setBackoffCriteria(long, int); - method public android.app.task.Task.Builder setExtras(android.os.PersistableBundle); - method public android.app.task.Task.Builder setMinimumLatency(long); - method public android.app.task.Task.Builder setOverrideDeadline(long); - method public android.app.task.Task.Builder setPeriodic(long); - method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int); - method public android.app.task.Task.Builder setRequiresCharging(boolean); - method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean); + public static final class JobInfo.Builder { + ctor public JobInfo.Builder(int, android.content.ComponentName); + method public android.app.job.JobInfo build(); + method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int); + method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle); + method public android.app.job.JobInfo.Builder setMinimumLatency(long); + method public android.app.job.JobInfo.Builder setOverrideDeadline(long); + method public android.app.job.JobInfo.Builder setPeriodic(long); + method public android.app.job.JobInfo.Builder setRequiredNetworkCapabilities(int); + method public android.app.job.JobInfo.Builder setRequiresCharging(boolean); + method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean); } - public static abstract interface Task.NetworkType { + public static abstract interface JobInfo.NetworkType { field public static final int ANY = 1; // 0x1 field public static final int NONE = 0; // 0x0 field public static final int UNMETERED = 2; // 0x2 } - public abstract class TaskManager { - ctor public TaskManager(); - method public abstract void cancel(int); - method public abstract void cancelAll(); - method public abstract java.util.List<android.app.task.Task> getAllPendingTasks(); - method public abstract int schedule(android.app.task.Task); - field public static final int RESULT_FAILURE = 0; // 0x0 - field public static final int RESULT_SUCCESS = 1; // 0x1 - } - - public class TaskParams implements android.os.Parcelable { + public class JobParameters implements android.os.Parcelable { method public int describeContents(); method public android.os.PersistableBundle getExtras(); - method public int getTaskId(); + method public int getJobId(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } - public abstract class TaskService extends android.app.Service { - ctor public TaskService(); + public abstract class JobScheduler { + ctor public JobScheduler(); + method public abstract void cancel(int); + method public abstract void cancelAll(); + method public abstract java.util.List<android.app.job.JobInfo> getAllPendingJobs(); + method public abstract int schedule(android.app.job.JobInfo); + field public static final int RESULT_FAILURE = 0; // 0x0 + field public static final int RESULT_SUCCESS = 1; // 0x1 + } + + public abstract class JobService extends android.app.Service { + ctor public JobService(); + method public final void jobFinished(android.app.job.JobParameters, boolean); method public final android.os.IBinder onBind(android.content.Intent); - method public abstract boolean onStartTask(android.app.task.TaskParams); - method public abstract boolean onStopTask(android.app.task.TaskParams); - method public final void taskFinished(android.app.task.TaskParams, boolean); - field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_TASK_SERVICE"; + method public abstract boolean onStartJob(android.app.job.JobParameters); + method public abstract boolean onStopJob(android.app.job.JobParameters); + field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE"; } } @@ -7020,6 +7019,7 @@ package android.content { field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; + field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler"; field public static final java.lang.String KEYGUARD_SERVICE = "keyguard"; field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps"; field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater"; @@ -7041,7 +7041,6 @@ package android.content { field public static final java.lang.String SEARCH_SERVICE = "search"; field public static final java.lang.String SENSOR_SERVICE = "sensor"; field public static final java.lang.String STORAGE_SERVICE = "storage"; - field public static final java.lang.String TASK_SERVICE = "task"; field public static final java.lang.String TELEPHONY_SERVICE = "phone"; field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices"; field public static final java.lang.String TV_INPUT_SERVICE = "tv_input"; @@ -8248,11 +8247,11 @@ package android.content.pm { } public static abstract interface LauncherApps.OnAppsChangedListener { - method public abstract void onPackageAdded(java.lang.String, android.os.UserHandle); - method public abstract void onPackageChanged(java.lang.String, android.os.UserHandle); - method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle); - method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean); - method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean); + method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String); + method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String); + method public abstract void onPackageRemoved(android.os.UserHandle, java.lang.String); + method public abstract void onPackagesAvailable(android.os.UserHandle, java.lang.String[], boolean); + method public abstract void onPackagesUnavailable(android.os.UserHandle, java.lang.String[], boolean); } public class PackageInfo implements android.os.Parcelable { @@ -11502,6 +11501,7 @@ package android.graphics.drawable { public class RippleDrawable extends android.graphics.drawable.LayerDrawable { ctor public RippleDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); + method public void setColor(android.content.res.ColorStateList); } public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback { @@ -13689,6 +13689,7 @@ package android.media { method public void adjustStreamVolume(int, int, int); method public void adjustSuggestedStreamVolume(int, int, int); method public void adjustVolume(int, int); + method public int allocateAudioSessionId(); method public void dispatchMediaKeyEvent(android.view.KeyEvent); method public int getMode(); method public java.lang.String getParameters(java.lang.String); @@ -13749,6 +13750,7 @@ package android.media { field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0 field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1 + field public static final int ERROR = -1; // 0xffffffff field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE"; field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"; field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE"; @@ -13928,6 +13930,7 @@ package android.media { method public static boolean hasProfile(int); method public static boolean hasProfile(int, int); field public static final int QUALITY_1080P = 6; // 0x6 + field public static final int QUALITY_2160P = 8; // 0x8 field public static final int QUALITY_480P = 4; // 0x4 field public static final int QUALITY_720P = 5; // 0x5 field public static final int QUALITY_CIF = 3; // 0x3 @@ -13936,6 +13939,7 @@ package android.media { field public static final int QUALITY_QCIF = 2; // 0x2 field public static final int QUALITY_QVGA = 7; // 0x7 field public static final int QUALITY_TIME_LAPSE_1080P = 1006; // 0x3ee + field public static final int QUALITY_TIME_LAPSE_2160P = 1008; // 0x3f0 field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed field public static final int QUALITY_TIME_LAPSE_CIF = 1003; // 0x3eb @@ -15826,8 +15830,11 @@ package android.media.tv { field public static final int TYPE_ISDB_S = 262656; // 0x40200 field public static final int TYPE_ISDB_T = 262144; // 0x40000 field public static final int TYPE_ISDB_TB = 262400; // 0x40100 + field public static final int TYPE_NTSC = 1; // 0x1 field public static final int TYPE_OTHER = 0; // 0x0 + field public static final int TYPE_PAL = 2; // 0x2 field public static final int TYPE_PASSTHROUGH = 65536; // 0x10000 + field public static final int TYPE_SECAM = 3; // 0x3 field public static final int TYPE_S_DMB = 393472; // 0x60100 field public static final int TYPE_T_DMB = 393216; // 0x60000 } @@ -15878,6 +15885,7 @@ package android.media.tv { method public android.content.Intent getIntentForSetupActivity(); method public android.content.pm.ServiceInfo getServiceInfo(); method public int getType(); + method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager); method public void writeToParcel(android.os.Parcel, int); field public static final java.lang.String EXTRA_SERVICE_NAME = "serviceName"; @@ -16088,28 +16096,25 @@ package android.net { method public deprecated int getNetworkPreference(); method public static android.net.Network getProcessDefaultNetwork(); method public boolean isActiveNetworkMetered(); - method public boolean isNetworkActive(); + method public boolean isDefaultNetworkActive(); method public static boolean isNetworkTypeValid(int); - method public android.net.NetworkRequest listenForNetwork(android.net.NetworkCapabilities, android.net.ConnectivityManager.NetworkCallbackListener); - method public void registerNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); - method public void releaseNetworkRequest(android.net.NetworkRequest); + method public void registerDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); + method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback); method public void reportBadNetwork(android.net.Network); - method public android.net.NetworkRequest requestNetwork(android.net.NetworkCapabilities, android.net.ConnectivityManager.NetworkCallbackListener); - method public android.net.NetworkRequest requestNetwork(android.net.NetworkCapabilities, android.app.PendingIntent); + method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback); method public deprecated boolean requestRouteToHost(int, int); method public deprecated void setNetworkPreference(int); method public static boolean setProcessDefaultNetwork(android.net.Network); method public deprecated int startUsingNetworkFeature(int, java.lang.String); method public deprecated int stopUsingNetworkFeature(int, java.lang.String); - method public void unregisterNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); + method public void unregisterDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); + method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback); field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1 field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo"; field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover"; field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; - field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork"; - field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES = "networkRequestNetworkCapabilities"; field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType"; field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity"; field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork"; @@ -16126,14 +16131,13 @@ package android.net { field public static final int TYPE_WIMAX = 6; // 0x6 } - public static class ConnectivityManager.NetworkCallbackListener { - ctor public ConnectivityManager.NetworkCallbackListener(); - method public void onAvailable(android.net.NetworkRequest, android.net.Network); - method public void onLinkPropertiesChanged(android.net.NetworkRequest, android.net.Network, android.net.LinkProperties); - method public void onLosing(android.net.NetworkRequest, android.net.Network, int); - method public void onLost(android.net.NetworkRequest, android.net.Network); - method public void onNetworkCapabilitiesChanged(android.net.NetworkRequest, android.net.Network, android.net.NetworkCapabilities); - method public void onReleased(android.net.NetworkRequest); + public static class ConnectivityManager.NetworkCallback { + ctor public ConnectivityManager.NetworkCallback(); + method public void onAvailable(android.net.Network); + method public void onCapabilitiesChanged(android.net.Network, android.net.NetworkCapabilities); + method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties); + method public void onLosing(android.net.Network, int); + method public void onLost(android.net.Network); } public static abstract interface ConnectivityManager.OnNetworkActiveListener { @@ -16160,12 +16164,13 @@ package android.net { field public int serverAddress; } - public class IpPrefix implements android.os.Parcelable { + public final class IpPrefix implements android.os.Parcelable { method public int describeContents(); method public java.net.InetAddress getAddress(); method public int getPrefixLength(); method public byte[] getRawAddress(); method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; } public class LinkAddress implements android.os.Parcelable { @@ -16177,7 +16182,7 @@ package android.net { method public void writeToParcel(android.os.Parcel, int); } - public class LinkProperties implements android.os.Parcelable { + public final class LinkProperties implements android.os.Parcelable { method public int describeContents(); method public java.util.List<java.net.InetAddress> getDnsServers(); method public java.lang.String getDomains(); @@ -16267,21 +16272,12 @@ package android.net { } public final class NetworkCapabilities implements android.os.Parcelable { - ctor public NetworkCapabilities(); ctor public NetworkCapabilities(android.net.NetworkCapabilities); - method public void addNetworkCapability(int); - method public void addTransportType(int); method public int describeContents(); method public int getLinkDownstreamBandwidthKbps(); method public int getLinkUpstreamBandwidthKbps(); - method public java.util.Collection<java.lang.Integer> getNetworkCapabilities(); - method public java.util.Collection<java.lang.Integer> getTransportTypes(); method public boolean hasCapability(int); method public boolean hasTransport(int); - method public void removeNetworkCapability(int); - method public void removeTransportType(int); - method public void setLinkDownstreamBandwidthKbps(int); - method public void setLinkUpstreamBandwidthKbps(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int NET_CAPABILITY_CBS = 5; // 0x5 @@ -16355,7 +16351,15 @@ package android.net { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; - field public final android.net.NetworkCapabilities networkCapabilities; + } + + public static class NetworkRequest.Builder { + ctor public NetworkRequest.Builder(); + method public android.net.NetworkRequest.Builder addCapability(int); + method public android.net.NetworkRequest.Builder addTransportType(int); + method public android.net.NetworkRequest build(); + method public android.net.NetworkRequest.Builder removeCapability(int); + method public android.net.NetworkRequest.Builder removeTransportType(int); } public class ParseException extends java.lang.RuntimeException { @@ -16384,7 +16388,7 @@ package android.net { method public void writeToParcel(android.os.Parcel, int); } - public class RouteInfo implements android.os.Parcelable { + public final class RouteInfo implements android.os.Parcelable { method public int describeContents(); method public android.net.IpPrefix getDestination(); method public java.net.InetAddress getGateway(); @@ -16392,6 +16396,7 @@ package android.net { method public boolean isDefaultRoute(); method public boolean matches(java.net.InetAddress); method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; } public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory { @@ -26016,21 +26021,28 @@ package android.service.notification { method public final void cancelNotification(java.lang.String); method public final void cancelNotifications(java.lang.String[]); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); - method public android.service.notification.NotificationListenerService.Ranking getCurrentRanking(); + method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking(); method public android.os.IBinder onBind(android.content.Intent); method public void onListenerConnected(); - method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification); - method public void onNotificationRankingUpdate(); - method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification); + method public void onNotificationPosted(android.service.notification.StatusBarNotification); + method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); + method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap); + method public void onNotificationRemoved(android.service.notification.StatusBarNotification); + method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; } - public static class NotificationListenerService.Ranking implements android.os.Parcelable { + public static class NotificationListenerService.Ranking { + method public java.lang.String getKey(); + method public int getRank(); + method public boolean isAmbient(); + method public boolean isInterceptedByDoNotDisturb(); + } + + public static class NotificationListenerService.RankingMap implements android.os.Parcelable { method public int describeContents(); method public java.lang.String[] getOrderedKeys(); - method public int getRank(java.lang.String); - method public boolean isAmbient(java.lang.String); - method public boolean isInterceptedByDoNotDisturb(java.lang.String); + method public android.service.notification.NotificationListenerService.Ranking getRanking(java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } @@ -33509,6 +33521,7 @@ package android.view { method public android.transition.Transition getSharedElementEnterTransition(); method public android.transition.Transition getSharedElementExitTransition(); method public abstract int getStatusBarColor(); + method public long getTransitionBackgroundFadeDuration(); method public android.transition.TransitionManager getTransitionManager(); method public abstract int getVolumeControlStream(); method public android.view.WindowManager getWindowManager(); @@ -33567,6 +33580,7 @@ package android.view { method public abstract void setStatusBarColor(int); method public abstract void setTitle(java.lang.CharSequence); method public abstract deprecated void setTitleColor(int); + method public void setTransitionBackgroundFadeDuration(long); method public void setTransitionManager(android.transition.TransitionManager); method public void setType(int); method public void setUiOptions(int); @@ -34581,15 +34595,15 @@ package android.view.inputmethod { field public static final android.os.Parcelable.Creator CREATOR; } - public static final class CursorAnchorInfo.CursorAnchorInfoBuilder { - ctor public CursorAnchorInfo.CursorAnchorInfoBuilder(); - method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder addCharacterRect(int, float, float, float, float); + public static final class CursorAnchorInfo.Builder { + ctor public CursorAnchorInfo.Builder(); + method public android.view.inputmethod.CursorAnchorInfo.Builder addCharacterRect(int, float, float, float, float); method public android.view.inputmethod.CursorAnchorInfo build(); method public void reset(); - method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setComposingText(int, java.lang.CharSequence); - method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setInsertionMarkerLocation(float, float, float, float); - method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setMatrix(android.graphics.Matrix); - method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setSelectionRange(int, int); + method public android.view.inputmethod.CursorAnchorInfo.Builder setComposingText(int, java.lang.CharSequence); + method public android.view.inputmethod.CursorAnchorInfo.Builder setInsertionMarkerLocation(float, float, float, float); + method public android.view.inputmethod.CursorAnchorInfo.Builder setMatrix(android.graphics.Matrix); + method public android.view.inputmethod.CursorAnchorInfo.Builder setSelectionRange(int, int); } public class EditorInfo implements android.text.InputType android.os.Parcelable { diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java index dce0a75..e6847a9 100644 --- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java +++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java @@ -20,6 +20,7 @@ import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.IActivityManager.ContentProviderHolder; import android.content.IContentProvider; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -33,7 +34,8 @@ public final class SettingsCmd { enum CommandVerb { UNSPECIFIED, GET, - PUT + PUT, + DELETE } static String[] mArgs; @@ -74,6 +76,8 @@ public final class SettingsCmd { mVerb = CommandVerb.GET; } else if ("put".equalsIgnoreCase(arg)) { mVerb = CommandVerb.PUT; + } else if ("delete".equalsIgnoreCase(arg)) { + mVerb = CommandVerb.DELETE; } else { // invalid System.err.println("Invalid command: " + arg); @@ -87,7 +91,7 @@ public final class SettingsCmd { break; // invalid } mTable = arg.toLowerCase(); - } else if (mVerb == CommandVerb.GET) { + } else if (mVerb == CommandVerb.GET || mVerb == CommandVerb.DELETE) { mKey = arg; if (mNextArg >= mArgs.length) { valid = true; @@ -136,6 +140,10 @@ public final class SettingsCmd { case PUT: putForUser(provider, mUser, mTable, mKey, mValue); break; + case DELETE: + System.out.println("Deleted " + + deleteForUser(provider, mUser, mTable, mKey) + " rows"); + break; default: System.err.println("Unspecified command"); break; @@ -211,9 +219,31 @@ public final class SettingsCmd { } } + int deleteForUser(IContentProvider provider, int userHandle, + final String table, final String key) { + Uri targetUri; + if ("system".equals(table)) targetUri = Settings.System.getUriFor(key); + else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key); + else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key); + else { + System.err.println("Invalid table; no delete performed"); + throw new IllegalArgumentException("Invalid table " + table); + } + + int num = 0; + try { + num = provider.delete(null, targetUri, null, null); + } catch (RemoteException e) { + System.err.println("Can't clear key " + key + " in " + table + " for user " + + userHandle); + } + return num; + } + private static void printUsage() { System.err.println("usage: settings [--user NUM] get namespace key"); System.err.println(" settings [--user NUM] put namespace key value"); + System.err.println(" settings [--user NUM] delete namespace key"); System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive"); System.err.println("If '--user NUM' is not given, the operations are performed on the owner user."); } diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 2620c44..cbc8150 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -462,7 +462,9 @@ public abstract class AccessibilityService extends Service { * anything behind it, then only the modal window will be reported * (assuming it is the top one). For convenience the returned windows * are ordered in a descending layer order, which is the windows that - * are higher in the Z-order are reported first. + * are higher in the Z-order are reported first. Since the user can always + * interact with the window that has input focus by typing, the focused + * window is always returned (even if covered by a modal window). * <p> * <strong>Note:</strong> In order to access the windows your service has * to declare the capability to retrieve window content by setting the diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 4f9ba59..4edb0c6 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -286,8 +286,8 @@ public class AccessibilityServiceInfo implements Parcelable { /** * This flag indicates to the system that the accessibility service wants * to access content of all interactive windows. An interactive window is a - * window that can be touched by a sighted user when explore by touch is not - * enabled. If this flag is not set your service will not receive + * window that has input focus or can be touched by a sighted user when explore + * by touch is not enabled. If this flag is not set your service will not receive * {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED} * events, calling AccessibilityService{@link AccessibilityService#getWindows() * AccessibilityService.getWindows()} will return an empty list, and {@link diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index 933135d..06f5aca 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -17,6 +17,7 @@ package android.animation; import android.content.Context; import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.content.res.Resources.NotFoundException; @@ -25,6 +26,9 @@ import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import android.view.animation.AnimationUtils; + +import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -66,11 +70,26 @@ public class AnimatorInflater { */ public static Animator loadAnimator(Context context, int id) throws NotFoundException { + return loadAnimator(context.getResources(), context.getTheme(), id); + } + + /** + * Loads an {@link Animator} object from a resource + * + * @param resources The resources + * @param theme The theme + * @param id The resource id of the animation to load + * @return The animator object reference by the specified id + * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded + * @hide + */ + public static Animator loadAnimator(Resources resources, Theme theme, int id) + throws NotFoundException { XmlResourceParser parser = null; try { - parser = context.getResources().getAnimation(id); - return createAnimatorFromXml(context, parser); + parser = resources.getAnimation(id); + return createAnimatorFromXml(resources, theme, parser); } catch (XmlPullParserException ex) { Resources.NotFoundException rnf = new Resources.NotFoundException("Can't load animation resource ID #0x" + @@ -150,7 +169,8 @@ public class AnimatorInflater { } if (animator == null) { - animator = createAnimatorFromXml(context, parser); + animator = createAnimatorFromXml(context.getResources(), + context.getTheme(), parser); } if (animator == null) { @@ -166,103 +186,8 @@ public class AnimatorInflater { } } - private static Animator createAnimatorFromXml(Context c, XmlPullParser parser) - throws XmlPullParserException, IOException { - return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0); - } - - private static Animator createAnimatorFromXml(Context c, XmlPullParser parser, - AttributeSet attrs, AnimatorSet parent, int sequenceOrdering) - throws XmlPullParserException, IOException { - - Animator anim = null; - ArrayList<Animator> childAnims = null; - - // Make sure we are on a start tag. - int type; - int depth = parser.getDepth(); - - while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) - && type != XmlPullParser.END_DOCUMENT) { - - if (type != XmlPullParser.START_TAG) { - continue; - } - - String name = parser.getName(); - - if (name.equals("objectAnimator")) { - anim = loadObjectAnimator(c, attrs); - } else if (name.equals("animator")) { - anim = loadAnimator(c, attrs, null); - } else if (name.equals("set")) { - anim = new AnimatorSet(); - TypedArray a = c.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.AnimatorSet); - int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering, - TOGETHER); - createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim, ordering); - a.recycle(); - } else { - throw new RuntimeException("Unknown animator name: " + parser.getName()); - } - - if (parent != null) { - if (childAnims == null) { - childAnims = new ArrayList<Animator>(); - } - childAnims.add(anim); - } - } - if (parent != null && childAnims != null) { - Animator[] animsArray = new Animator[childAnims.size()]; - int index = 0; - for (Animator a : childAnims) { - animsArray[index++] = a; - } - if (sequenceOrdering == TOGETHER) { - parent.playTogether(animsArray); - } else { - parent.playSequentially(animsArray); - } - } - - return anim; - - } - - private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs) - throws NotFoundException { - - ObjectAnimator anim = new ObjectAnimator(); - - loadAnimator(context, attrs, anim); - - TypedArray a = - context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator); - - String propertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName); - - anim.setPropertyName(propertyName); - - a.recycle(); - - return anim; - } - - /** - * Creates a new animation whose parameters come from the specified context and - * attributes set. - * - * @param context the application environment - * @param attrs the set of attributes holding the animation parameters - */ - private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim) - throws NotFoundException { - - TypedArray a = - context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator); + private static void parseAnimatorFromTypeArray(ValueAnimator anim, TypedArray a) { long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 300); long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0); @@ -378,11 +303,123 @@ public class AnimatorInflater { if (evaluator != null) { anim.setEvaluator(evaluator); } + } + + private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser) + throws XmlPullParserException, IOException { + return createAnimatorFromXml(res, theme, parser, Xml.asAttributeSet(parser), null, 0); + } + + private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser, + AttributeSet attrs, AnimatorSet parent, int sequenceOrdering) + throws XmlPullParserException, IOException { + + Animator anim = null; + ArrayList<Animator> childAnims = null; + + // Make sure we are on a start tag. + int type; + int depth = parser.getDepth(); + + while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) + && type != XmlPullParser.END_DOCUMENT) { + + if (type != XmlPullParser.START_TAG) { + continue; + } + + String name = parser.getName(); + + if (name.equals("objectAnimator")) { + anim = loadObjectAnimator(res, theme, attrs); + } else if (name.equals("animator")) { + anim = loadAnimator(res, theme, attrs, null); + } else if (name.equals("set")) { + anim = new AnimatorSet(); + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimatorSet, 0, 0); + } else { + a = res.obtainAttributes(attrs, com.android.internal.R.styleable.AnimatorSet); + } + int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering, + TOGETHER); + createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering); + a.recycle(); + } else { + throw new RuntimeException("Unknown animator name: " + parser.getName()); + } + + if (parent != null) { + if (childAnims == null) { + childAnims = new ArrayList<Animator>(); + } + childAnims.add(anim); + } + } + if (parent != null && childAnims != null) { + Animator[] animsArray = new Animator[childAnims.size()]; + int index = 0; + for (Animator a : childAnims) { + animsArray[index++] = a; + } + if (sequenceOrdering == TOGETHER) { + parent.playTogether(animsArray); + } else { + parent.playSequentially(animsArray); + } + } + + return anim; + + } + + private static ObjectAnimator loadObjectAnimator(Resources res, Theme theme, AttributeSet attrs) + throws NotFoundException { + ObjectAnimator anim = new ObjectAnimator(); + + loadAnimator(res, theme, attrs, anim); + + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.PropertyAnimator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.PropertyAnimator); + } + + String propertyName = a.getString(R.styleable.PropertyAnimator_propertyName); + + anim.setPropertyName(propertyName); + + a.recycle(); + + return anim; + } + + /** + * Creates a new animation whose parameters come from the specified context + * and attributes set. + * + * @param res The resources + * @param attrs The set of attributes holding the animation parameters + */ + private static ValueAnimator loadAnimator(Resources res, Theme theme, + AttributeSet attrs, ValueAnimator anim) + throws NotFoundException { + + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.Animator); + } + + parseAnimatorFromTypeArray(anim, a); final int resID = a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0); if (resID > 0) { - anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID)); + anim.setInterpolator(AnimationUtils.loadInterpolator(res, theme, resID)); } a.recycle(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 23b5f29..f6883e2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5326,7 +5326,8 @@ public class Activity extends ContextThemeWrapper * * @hide */ - void convertToTranslucent(TranslucentConversionListener callback, ActivityOptions options) { + public void convertToTranslucent(TranslucentConversionListener callback, + ActivityOptions options) { boolean drawComplete; try { mTranslucentCallback = callback; @@ -5615,6 +5616,34 @@ public class Activity extends ContextThemeWrapper mExitTransitionListener = listener; } + /** + * Postpone the entering activity transition when Activity was started with + * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, + * android.util.Pair[])}. + * <p>This method gives the Activity the ability to delay starting the entering and + * shared element transitions until all data is loaded. Until then, the Activity won't + * draw into its window, leaving the window transparent. This may also cause the + * returning animation to be delayed until data is ready. This method should be + * called in {@link #onCreate(android.os.Bundle)} or in + * {@link #onActivityReenter(int, android.content.Intent)}. + * {@link #startPostponedEnterTransition()} must be called to allow the Activity to + * start the transitions. If the Activity did not use + * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, + * android.util.Pair[])}, then this method does nothing.</p> + */ + public void postponeEnterTransition() { + mActivityTransitionState.postponeEnterTransition(); + } + + /** + * Begin postponed transitions after {@link #postponeEnterTransition()} was called. + * If postponeEnterTransition() was called, you must call startPostponedEnterTransition() + * to have your Activity start drawing. + */ + public void startPostponedEnterTransition() { + mActivityTransitionState.startPostponedEnterTransition(); + } + // ------------------ Internal API ------------------ final void setParent(Activity parent) { diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index a057c3e..9160452 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -394,8 +394,18 @@ public class ActivityOptions { if (sharedElements != null) { for (int i = 0; i < sharedElements.length; i++) { Pair<View, String> sharedElement = sharedElements[i]; - names.add(sharedElement.second); - mappedNames.add(sharedElement.first.getViewName()); + String sharedElementName = sharedElement.second; + if (sharedElementName == null) { + throw new IllegalArgumentException("Shared element name must not be null"); + } + String viewName = sharedElement.first.getViewName(); + if (viewName == null) { + throw new IllegalArgumentException("Shared elements must have non-null " + + "viewNames"); + } + + names.add(sharedElementName); + mappedNames.add(viewName); } } diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index a4384f8..0cccedc 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -129,9 +129,6 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { protected static final String KEY_SCALE_TYPE = "shared_element:scaleType"; protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix"; - // The background fade in/out duration. TODO: Enable tuning this. - public static final int FADE_BACKGROUND_DURATION_MS = 300; - protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values(); /** @@ -214,11 +211,21 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { ArrayList<String> allSharedElementNames, ArrayList<String> accepted, ArrayList<String> localNames, SharedElementListener listener, boolean isReturning) { + this(window, allSharedElementNames, listener, isReturning); + viewsReady(accepted, localNames); + } + + public ActivityTransitionCoordinator(Window window, + ArrayList<String> allSharedElementNames, + SharedElementListener listener, boolean isReturning) { super(new Handler()); mWindow = window; mListener = listener; mAllSharedElementNames = allSharedElementNames; mIsReturning = isReturning; + } + + protected void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) { setSharedElements(accepted, localNames); if (getViewsTransition() != null) { getDecor().captureTransitioningViews(mTransitioningViews); @@ -269,6 +276,8 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return names; } + public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; } + public static void setViewVisibility(Collection<View> views, int visibility) { if (views != null) { for (View view : views) { @@ -335,6 +344,10 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { String name = mAllSharedElementNames.get(i); View sharedElement = sharedElements.get(name); if (sharedElement != null) { + if (sharedElement.getViewName() == null) { + throw new IllegalArgumentException("Shared elements must have " + + "non-null viewNames"); + } mSharedElementNames.add(name); mSharedElements.add(sharedElement); } @@ -496,6 +509,10 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return bundle; } + protected long getFadeDuration() { + return getWindow().getTransitionBackgroundFadeDuration(); + } + /** * Captures placement information for Views with a shared element name for * Activity Transitions. diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index b32e9ad..d94dadd 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -87,6 +87,11 @@ class ActivityTransitionState { */ private boolean mHasExited; + /** + * Postpone painting and starting the enter transition until this is false. + */ + private boolean mIsEnterPostponed; + public ActivityTransitionState() { } @@ -140,15 +145,38 @@ class ActivityTransitionState { if (mEnterActivityOptions.isReturning()) { restoreExitedViews(); activity.getWindow().getDecorView().setVisibility(View.VISIBLE); - mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, - resultReceiver, sharedElementNames, mExitingFrom, mExitingTo); + } + mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, + resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning()); + + if (!mIsEnterPostponed) { + startEnter(); + } + } + + public void postponeEnterTransition() { + mIsEnterPostponed = true; + } + + public void startPostponedEnterTransition() { + if (mIsEnterPostponed) { + mIsEnterPostponed = false; + if (mEnterTransitionCoordinator != null) { + startEnter(); + } + } + } + + private void startEnter() { + if (mEnterActivityOptions.isReturning()) { + mEnterTransitionCoordinator.viewsReady(mExitingFrom, mExitingTo); } else { - mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, - resultReceiver, sharedElementNames, null, null); - mEnteringNames = sharedElementNames; + mEnterTransitionCoordinator.viewsReady(null, null); + mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames(); mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames(); mEnteringTo = mEnterTransitionCoordinator.getMappedNames(); } + mExitingFrom = null; mExitingTo = null; mEnterActivityOptions = null; diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index c29d75e..94ea2c5 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -354,9 +354,11 @@ public class ActivityView extends ViewGroup { private static class ActivityContainerWrapper { private final IActivityContainer mIActivityContainer; private final CloseGuard mGuard = CloseGuard.get(); + boolean mOpened; // Protected by mGuard. ActivityContainerWrapper(IActivityContainer container) { mIActivityContainer = container; + mOpened = true; mGuard.open("release"); } @@ -424,11 +426,16 @@ public class ActivityView extends ViewGroup { } void release() { - if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called"); - try { - mIActivityContainer.release(); - mGuard.close(); - } catch (RemoteException e) { + synchronized (mGuard) { + if (mOpened) { + if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called"); + try { + mIActivityContainer.release(); + mGuard.close(); + } catch (RemoteException e) { + } + mOpened = false; + } } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ad506e4..52d4585 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -135,7 +135,7 @@ import android.view.textservice.TextServicesManager; import android.accounts.AccountManager; import android.accounts.IAccountManager; import android.app.admin.DevicePolicyManager; -import android.app.task.ITaskManager; +import android.app.job.IJobScheduler; import android.app.trust.TrustManager; import com.android.internal.annotations.GuardedBy; @@ -706,10 +706,10 @@ class ContextImpl extends Context { return new UsageStatsManager(ctx.getOuterContext()); }}); - registerService(TASK_SERVICE, new ServiceFetcher() { + registerService(JOB_SCHEDULER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { - IBinder b = ServiceManager.getService(TASK_SERVICE); - return new TaskManagerImpl(ITaskManager.Stub.asInterface(b)); + IBinder b = ServiceManager.getService(JOB_SCHEDULER_SERVICE); + return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b)); }}); } diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index 4b052e7..f54cb87 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -53,18 +53,38 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private boolean mIsCanceled; private ObjectAnimator mBackgroundAnimator; private boolean mIsExitTransitionComplete; + private boolean mIsReadyForTransition; + private Bundle mSharedElementsBundle; public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, - ArrayList<String> sharedElementNames, - ArrayList<String> acceptedNames, ArrayList<String> mappedNames) { - super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames, - getListener(activity, acceptedNames), acceptedNames != null); + ArrayList<String> sharedElementNames, boolean isReturning) { + super(activity.getWindow(), sharedElementNames, + getListener(activity, isReturning), isReturning); mActivity = activity; setResultReceiver(resultReceiver); prepareEnter(); Bundle resultReceiverBundle = new Bundle(); resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this); mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle); + getDecor().getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + if (mIsReadyForTransition) { + getDecor().getViewTreeObserver().removeOnPreDrawListener(this); + } + return mIsReadyForTransition; + } + }); + } + + public void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) { + if (mIsReadyForTransition) { + return; + } + super.viewsReady(accepted, localNames); + + mIsReadyForTransition = true; if (mIsReturning) { mHandler = new Handler() { @Override @@ -75,6 +95,13 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS); send(MSG_SEND_SHARED_ELEMENT_DESTINATION, null); } + setViewVisibility(mSharedElements, View.INVISIBLE); + if (getViewsTransition() != null) { + setViewVisibility(mTransitioningViews, View.INVISIBLE); + } + if (mSharedElementsBundle != null) { + onTakeSharedElements(); + } } private void sendSharedElementDestination() { @@ -94,9 +121,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } } - private static SharedElementListener getListener(Activity activity, - ArrayList<String> acceptedNames) { - boolean isReturning = acceptedNames != null; + private static SharedElementListener getListener(Activity activity, boolean isReturning) { return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener; } @@ -108,7 +133,8 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { if (mHandler != null) { mHandler.removeMessages(MSG_CANCEL); } - onTakeSharedElements(resultData); + mSharedElementsBundle = resultData; + onTakeSharedElements(); } break; case MSG_EXIT_TRANSITION_COMPLETE: @@ -139,7 +165,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { mSharedElementNames.clear(); mSharedElements.clear(); mAllSharedElementNames.clear(); - onTakeSharedElements(null); + startSharedElementTransition(null); onRemoteExitTransitionComplete(); } } @@ -149,10 +175,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } protected void prepareEnter() { - setViewVisibility(mSharedElements, View.INVISIBLE); - if (getViewsTransition() != null) { - setViewVisibility(mTransitioningViews, View.INVISIBLE); - } mActivity.overridePendingTransition(0, 0); if (!mIsReturning) { mActivity.convertToTranslucent(null, null); @@ -185,7 +207,25 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } } - protected void onTakeSharedElements(Bundle sharedElementState) { + protected void onTakeSharedElements() { + if (!mIsReadyForTransition || mSharedElementsBundle == null) { + return; + } + final Bundle sharedElementState = mSharedElementsBundle; + mSharedElementsBundle = null; + getDecor().getViewTreeObserver() + .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + getDecor().getViewTreeObserver().removeOnPreDrawListener(this); + startSharedElementTransition(sharedElementState); + return false; + } + }); + getDecor().invalidate(); + } + + private void startSharedElementTransition(Bundle sharedElementState) { setEpicenter(); // Remove rejected shared elements ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames); @@ -276,7 +316,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { if (background != null) { background = background.mutate(); mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 255); - mBackgroundAnimator.setDuration(FADE_BACKGROUND_DURATION_MS); + mBackgroundAnimator.setDuration(getFadeDuration()); mBackgroundAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -299,8 +339,8 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } public void stop() { + makeOpaque(); mHasStopped = true; - mActivity = null; mIsCanceled = true; mResultReceiver = null; if (mBackgroundAnimator != null) { @@ -310,7 +350,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } private void makeOpaque() { - if (!mHasStopped) { + if (!mHasStopped && mActivity != null) { mActivity.convertFromTranslucent(); mActivity = null; } diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index ba1638f..8d5b831 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -199,7 +199,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } } }); - mBackgroundAnimator.setDuration(FADE_BACKGROUND_DURATION_MS); + mBackgroundAnimator.setDuration(getFadeDuration()); mBackgroundAnimator.start(); } } diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/JobSchedulerImpl.java index fe29fb7..09038d5 100644 --- a/core/java/android/app/TaskManagerImpl.java +++ b/core/java/android/app/JobSchedulerImpl.java @@ -17,38 +17,38 @@ // in android.app so ContextImpl has package access package android.app; -import android.app.task.ITaskManager; -import android.app.task.Task; -import android.app.task.TaskManager; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.app.job.IJobScheduler; import android.os.RemoteException; import java.util.List; /** - * Concrete implementation of the TaskManager interface + * Concrete implementation of the JobScheduler interface * @hide */ -public class TaskManagerImpl extends TaskManager { - ITaskManager mBinder; +public class JobSchedulerImpl extends JobScheduler { + IJobScheduler mBinder; - /* package */ TaskManagerImpl(ITaskManager binder) { + /* package */ JobSchedulerImpl(IJobScheduler binder) { mBinder = binder; } @Override - public int schedule(Task task) { + public int schedule(JobInfo job) { try { - return mBinder.schedule(task); + return mBinder.schedule(job); } catch (RemoteException e) { - return TaskManager.RESULT_FAILURE; + return JobScheduler.RESULT_FAILURE; } } @Override - public void cancel(int taskId) { + public void cancel(int jobId) { try { - mBinder.cancel(taskId); + mBinder.cancel(jobId); } catch (RemoteException e) {} } @@ -62,9 +62,9 @@ public class TaskManagerImpl extends TaskManager { } @Override - public List<Task> getAllPendingTasks() { + public List<JobInfo> getAllPendingJobs() { try { - return mBinder.getAllPendingTasks(); + return mBinder.getAllPendingJobs(); } catch (RemoteException e) { return null; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a1cdf59..276f936 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; @@ -31,7 +30,6 @@ import android.media.AudioManager; import android.media.session.MediaSessionToken; import android.net.Uri; import android.os.BadParcelableException; -import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -2568,7 +2566,7 @@ public class Notification implements Parcelable R.id.icon, true, -1, - mColor, + resolveColor(), PorterDuff.Mode.SRC_ATOP, -1); } @@ -2595,17 +2593,22 @@ public class Notification implements Parcelable R.id.right_icon, true, -1, - mColor, + resolveColor(), PorterDuff.Mode.SRC_ATOP, -1); } } + private int sanitizeColor() { + if (mColor != COLOR_DEFAULT) { + mColor |= 0xFF000000; // no alpha for custom colors + } + return mColor; + } + private int resolveColor() { if (mColor == COLOR_DEFAULT) { - mColor = mContext.getResources().getColor(R.color.notification_icon_bg_color); - } else { - mColor |= 0xFF000000; // no alpha for custom colors + return mContext.getResources().getColor(R.color.notification_icon_bg_color); } return mColor; } @@ -2621,7 +2624,7 @@ public class Notification implements Parcelable n.iconLevel = mSmallIconLevel; n.number = mNumber; - n.color = resolveColor(); + n.color = sanitizeColor(); n.contentView = makeContentView(); n.contentIntent = mContentIntent; diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index 85e970c..0d94721 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -293,7 +293,7 @@ public class VoiceInteractor { IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, IVoiceInteractorCallback callback) throws RemoteException { - return interactor.startConfirmation(packageName, callback, mCommand, mArgs); + return interactor.startCommand(packageName, callback, mCommand, mArgs); } } diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java index 1015514..45a2625 100644 --- a/core/java/android/app/admin/DeviceAdminReceiver.java +++ b/core/java/android/app/admin/DeviceAdminReceiver.java @@ -222,6 +222,12 @@ public class DeviceAdminReceiver extends BroadcastReceiver { * Called after the administrator is first enabled, as a result of * receiving {@link #ACTION_DEVICE_ADMIN_ENABLED}. At this point you * can use {@link DevicePolicyManager} to set your desired policies. + * + * <p> If the admin is activated by a device owner, then the intent + * may contain private extras that are relevant to user setup. + * {@see DevicePolicyManager#createAndInitializeUser(ComponentName, String, String, + * ComponentName, Intent)} + * * @param context The running context as per {@link #onReceive}. * @param intent The received intent as per {@link #onReceive}. */ diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 4aa4294..e80c761 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -136,7 +136,7 @@ public class DevicePolicyManager { * @hide */ public static final String ACTION_PROVISIONING_USER_HAS_CONSENTED - = "android.app.action.USER_HAS_CONSENTED"; + = "android.app.action.ACTION_PROVISIONING_USER_HAS_CONSENTED"; /** * A String extra holding the name of the package of the mobile device management application @@ -2111,6 +2111,41 @@ public class DevicePolicyManager { } /** + * Called by a device owner to create a user with the specified name. The UserHandle returned + * by this method should not be persisted as user handles are recycled as users are removed and + * created. If you need to persist an identifier for this user, use + * {@link UserManager#getSerialNumberForUser}. The new user will be started in the background + * immediately. + * + * <p> profileOwnerComponent is the {@link DeviceAdminReceiver} to be the profile owner as well + * as registered as an active admin on the new user. The profile owner package will be + * installed on the new user if it already is installed on the device. + * + * <p>If the optionalInitializeData is not null, then the extras will be passed to the + * profileOwnerComponent when onEnable is called. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param name the user's name + * @param ownerName the human readable name of the organisation associated with this DPM. + * @param profileOwnerComponent The {@link DeviceAdminReceiver} that will be an active admin on + * the user. + * @param adminExtras Extras that will be passed to onEnable of the admin receiver + * on the new user. + * @see UserHandle + * @return the UserHandle object for the created user, or null if the user could not be created. + */ + public UserHandle createAndInitializeUser(ComponentName admin, String name, String ownerName, + ComponentName profileOwnerComponent, Bundle adminExtras) { + try { + return mService.createAndInitializeUser(admin, name, ownerName, profileOwnerComponent, + adminExtras); + } catch (RemoteException re) { + Log.w(TAG, "Could not create a user", re); + } + return null; + } + + /** * Called by a device owner to remove a user and all associated data. The primary user can * not be removed. * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index f8df780..a1caa21 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -136,6 +136,7 @@ interface IDevicePolicyManager { boolean isApplicationBlocked(in ComponentName admin, in String packageName); UserHandle createUser(in ComponentName who, in String name); + UserHandle createAndInitializeUser(in ComponentName who, in String name, in String profileOwnerName, in ComponentName profileOwnerComponent, in Bundle adminExtras); boolean removeUser(in ComponentName who, in UserHandle userHandle); void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled); diff --git a/core/java/android/app/task/ITaskCallback.aidl b/core/java/android/app/job/IJobCallback.aidl index d8a32fd..2d3948f 100644 --- a/core/java/android/app/task/ITaskCallback.aidl +++ b/core/java/android/app/job/IJobCallback.aidl @@ -14,43 +14,40 @@ * limitations under the License. */ -package android.app.task; - -import android.app.task.ITaskService; -import android.app.task.TaskParams; +package android.app.job; /** - * The server side of the TaskManager IPC protocols. The app-side implementation + * The server side of the JobScheduler IPC protocols. The app-side implementation * invokes on this interface to indicate completion of the (asynchronous) instructions * issued by the server. * * In all cases, the 'who' parameter is the caller's service binder, used to track - * which Task Service instance is reporting. + * which Job Service instance is reporting. * * {@hide} */ -interface ITaskCallback { +interface IJobCallback { /** * Immediate callback to the system after sending a start signal, used to quickly detect ANR. * - * @param taskId Unique integer used to identify this task. - * @param ongoing True to indicate that the client is processing the task. False if the task is + * @param jobId Unique integer used to identify this job. + * @param ongoing True to indicate that the client is processing the job. False if the job is * complete */ - void acknowledgeStartMessage(int taskId, boolean ongoing); + void acknowledgeStartMessage(int jobId, boolean ongoing); /** * Immediate callback to the system after sending a stop signal, used to quickly detect ANR. * - * @param taskId Unique integer used to identify this task. - * @param rescheulde Whether or not to reschedule this task. + * @param jobId Unique integer used to identify this job. + * @param reschedule Whether or not to reschedule this job. */ - void acknowledgeStopMessage(int taskId, boolean reschedule); + void acknowledgeStopMessage(int jobId, boolean reschedule); /* - * Tell the task manager that the client is done with its execution, so that it can go on to + * Tell the job manager that the client is done with its execution, so that it can go on to * the next one and stop attributing wakelock time to us etc. * - * @param taskId Unique integer used to identify this task. - * @param reschedule Whether or not to reschedule this task. + * @param jobId Unique integer used to identify this job. + * @param reschedule Whether or not to reschedule this job. */ - void taskFinished(int taskId, boolean reschedule); + void jobFinished(int jobId, boolean reschedule); } diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/job/IJobScheduler.aidl index b56c78a..f1258ae 100644 --- a/core/java/android/app/task/ITaskManager.aidl +++ b/core/java/android/app/job/IJobScheduler.aidl @@ -14,17 +14,17 @@ * limitations under the License. */ -package android.app.task; +package android.app.job; -import android.app.task.Task; +import android.app.job.JobInfo; /** - * IPC interface that supports the app-facing {@link #TaskManager} api. + * IPC interface that supports the app-facing {@link #JobScheduler} api. * {@hide} */ -interface ITaskManager { - int schedule(in Task task); - void cancel(int taskId); +interface IJobScheduler { + int schedule(in JobInfo job); + void cancel(int jobId); void cancelAll(); - List<Task> getAllPendingTasks(); + List<JobInfo> getAllPendingJobs(); } diff --git a/core/java/android/app/task/ITaskService.aidl b/core/java/android/app/job/IJobService.aidl index 87b0191..63f8b81 100644 --- a/core/java/android/app/task/ITaskService.aidl +++ b/core/java/android/app/job/IJobService.aidl @@ -14,22 +14,19 @@ * limitations under the License. */ -package android.app.task; +package android.app.job; -import android.app.task.ITaskCallback; -import android.app.task.TaskParams; - -import android.os.Bundle; +import android.app.job.JobParameters; /** * Interface that the framework uses to communicate with application code that implements a - * TaskService. End user code does not implement this interface directly; instead, the app's - * service implementation will extend android.app.task.TaskService. + * JobService. End user code does not implement this interface directly; instead, the app's + * service implementation will extend android.app.job.JobService. * {@hide} */ -oneway interface ITaskService { - /** Begin execution of application's task. */ - void startTask(in TaskParams taskParams); +oneway interface IJobService { + /** Begin execution of application's job. */ + void startJob(in JobParameters jobParams); /** Stop execution of application's task. */ - void stopTask(in TaskParams taskParams); + void stopJob(in JobParameters jobParams); } diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/job/JobInfo.aidl index 1f25439..7b198a8 100644 --- a/core/java/android/app/task/Task.aidl +++ b/core/java/android/app/job/JobInfo.aidl @@ -14,7 +14,6 @@ * limitations under the License. */ -package android.app.task; +package android.app.job; -parcelable Task; -
\ No newline at end of file +parcelable JobInfo; diff --git a/core/java/android/app/task/Task.java b/core/java/android/app/job/JobInfo.java index 0e660b3..a22e4cd 100644 --- a/core/java/android/app/task/Task.java +++ b/core/java/android/app/job/JobInfo.java @@ -14,7 +14,7 @@ * limitations under the License */ -package android.app.task; +package android.app.job; import android.content.ComponentName; import android.os.Bundle; @@ -23,22 +23,22 @@ import android.os.Parcelable; import android.os.PersistableBundle; /** - * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the + * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the * parameters required to schedule work against the calling application. These are constructed - * using the {@link Task.Builder}. + * using the {@link JobInfo.Builder}. */ -public class Task implements Parcelable { +public class JobInfo implements Parcelable { public interface NetworkType { /** Default. */ public final int NONE = 0; - /** This task requires network connectivity. */ + /** This job requires network connectivity. */ public final int ANY = 1; - /** This task requires network connectivity that is unmetered. */ + /** This job requires network connectivity that is unmetered. */ public final int UNMETERED = 2; } /** - * Amount of backoff a task has initially by default, in milliseconds. + * Amount of backoff a job has initially by default, in milliseconds. * @hide. */ public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 5000L; @@ -63,7 +63,7 @@ public class Task implements Parcelable { public final int EXPONENTIAL = 1; } - private final int taskId; + private final int jobId; // TODO: Change this to use PersistableBundle when that lands in master. private final PersistableBundle extras; private final ComponentName service; @@ -80,10 +80,10 @@ public class Task implements Parcelable { private final int backoffPolicy; /** - * Unique task id associated with this class. This is assigned to your task by the scheduler. + * Unique job id associated with this class. This is assigned to your job by the scheduler. */ public int getId() { - return taskId; + return jobId; } /** @@ -94,43 +94,43 @@ public class Task implements Parcelable { } /** - * Name of the service endpoint that will be called back into by the TaskManager. + * Name of the service endpoint that will be called back into by the JobScheduler. */ public ComponentName getService() { return service; } /** - * Whether this task needs the device to be plugged in. + * Whether this job needs the device to be plugged in. */ public boolean isRequireCharging() { return requireCharging; } /** - * Whether this task needs the device to be in an Idle maintenance window. + * Whether this job needs the device to be in an Idle maintenance window. */ public boolean isRequireDeviceIdle() { return requireDeviceIdle; } /** - * See {@link android.app.task.Task.NetworkType} for a description of this value. + * See {@link android.app.job.JobInfo.NetworkType} for a description of this value. */ public int getNetworkCapabilities() { return networkCapabilities; } /** - * Set for a task that does not recur periodically, to specify a delay after which the task - * will be eligible for execution. This value is not set if the task recurs periodically. + * Set for a job that does not recur periodically, to specify a delay after which the job + * will be eligible for execution. This value is not set if the job recurs periodically. */ public long getMinLatencyMillis() { return minLatencyMillis; } /** - * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the task recurs + * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs * periodically. */ public long getMaxExecutionDelayMillis() { @@ -138,23 +138,23 @@ public class Task implements Parcelable { } /** - * Track whether this task will repeat with a given period. + * Track whether this job will repeat with a given period. */ public boolean isPeriodic() { return isPeriodic; } /** - * Set to the interval between occurrences of this task. This value is <b>not</b> set if the - * task does not recur periodically. + * Set to the interval between occurrences of this job. This value is <b>not</b> set if the + * job does not recur periodically. */ public long getIntervalMillis() { return intervalMillis; } /** - * The amount of time the TaskManager will wait before rescheduling a failed task. This value - * will be increased depending on the backoff policy specified at task creation time. Defaults + * The amount of time the JobScheduler will wait before rescheduling a failed job. This value + * will be increased depending on the backoff policy specified at job creation time. Defaults * to 5 seconds. */ public long getInitialBackoffMillis() { @@ -162,7 +162,7 @@ public class Task implements Parcelable { } /** - * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field + * See {@link android.app.job.JobInfo.BackoffPolicy} for an explanation of the values this field * can take. This defaults to exponential. */ public int getBackoffPolicy() { @@ -187,8 +187,8 @@ public class Task implements Parcelable { return hasLateConstraint; } - private Task(Parcel in) { - taskId = in.readInt(); + private JobInfo(Parcel in) { + jobId = in.readInt(); extras = in.readPersistableBundle(); service = in.readParcelable(null); requireCharging = in.readInt() == 1; @@ -204,10 +204,10 @@ public class Task implements Parcelable { hasLateConstraint = in.readInt() == 1; } - private Task(Task.Builder b) { - taskId = b.mTaskId; + private JobInfo(JobInfo.Builder b) { + jobId = b.mJobId; extras = b.mExtras; - service = b.mTaskService; + service = b.mJobService; requireCharging = b.mRequiresCharging; requireDeviceIdle = b.mRequiresDeviceIdle; networkCapabilities = b.mNetworkCapabilities; @@ -228,7 +228,7 @@ public class Task implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeInt(taskId); + out.writeInt(jobId); out.writePersistableBundle(extras); out.writeParcelable(service, flags); out.writeInt(requireCharging ? 1 : 0); @@ -244,23 +244,23 @@ public class Task implements Parcelable { out.writeInt(hasLateConstraint ? 1 : 0); } - public static final Creator<Task> CREATOR = new Creator<Task>() { + public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() { @Override - public Task createFromParcel(Parcel in) { - return new Task(in); + public JobInfo createFromParcel(Parcel in) { + return new JobInfo(in); } @Override - public Task[] newArray(int size) { - return new Task[size]; + public JobInfo[] newArray(int size) { + return new JobInfo[size]; } }; - /** Builder class for constructing {@link Task} objects. */ + /** Builder class for constructing {@link JobInfo} objects. */ public static final class Builder { - private int mTaskId; + private int mJobId; private PersistableBundle mExtras = PersistableBundle.EMPTY; - private ComponentName mTaskService; + private ComponentName mJobService; // Requirements. private boolean mRequiresCharging; private boolean mRequiresDeviceIdle; @@ -280,15 +280,15 @@ public class Task implements Parcelable { private boolean mBackoffPolicySet = false; /** - * @param taskId Application-provided id for this task. Subsequent calls to cancel, or - * tasks created with the same taskId, will update the pre-existing task with + * @param jobId Application-provided id for this job. Subsequent calls to cancel, or + * jobs created with the same jobId, will update the pre-existing job with * the same id. - * @param taskService The endpoint that you implement that will receive the callback from the - * TaskManager. + * @param jobService The endpoint that you implement that will receive the callback from the + * JobScheduler. */ - public Builder(int taskId, ComponentName taskService) { - mTaskService = taskService; - mTaskId = taskId; + public Builder(int jobId, ComponentName jobService) { + mJobService = jobService; + mJobId = jobId; } /** @@ -302,10 +302,10 @@ public class Task implements Parcelable { /** * Set some description of the kind of network capabilities you would like to have. This - * will be a parameter defined in {@link android.app.task.Task.NetworkType}. + * will be a parameter defined in {@link android.app.job.JobInfo.NetworkType}. * Not calling this function means the network is not necessary. * Bear in mind that calling this function defines network as a strict requirement for your - * task if the network requested is not available your task will never run. See + * job if the network requested is not available your job will never run. See * {@link #setOverrideDeadline(long)} to change this behaviour. */ public Builder setRequiredNetworkCapabilities(int networkCapabilities) { @@ -313,10 +313,10 @@ public class Task implements Parcelable { return this; } - /* - * Specify that to run this task, the device needs to be plugged in. This defaults to + /** + * Specify that to run this job, the device needs to be plugged in. This defaults to * false. - * @param requireCharging Whether or not the device is plugged in. + * @param requiresCharging Whether or not the device is plugged in. */ public Builder setRequiresCharging(boolean requiresCharging) { mRequiresCharging = requiresCharging; @@ -324,11 +324,11 @@ public class Task implements Parcelable { } /** - * Specify that to run, the task needs the device to be in idle mode. This defaults to + * Specify that to run, the job needs the device to be in idle mode. This defaults to * false. * <p>Idle mode is a loose definition provided by the system, which means that the device * is not in use, and has not been in use for some time. As such, it is a good time to - * perform resource heavy tasks. Bear in mind that battery usage will still be attributed + * perform resource heavy jobs. Bear in mind that battery usage will still be attributed * to your application, and surfaced to the user in battery stats.</p> * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance * window. @@ -339,17 +339,17 @@ public class Task implements Parcelable { } /** - * Specify that this task should recur with the provided interval, not more than once per - * period. You have no control over when within this interval this task will be executed, + * Specify that this job should recur with the provided interval, not more than once per + * period. You have no control over when within this interval this job will be executed, * only the guarantee that it will be executed at most once within this interval. - * A periodic task will be repeated until the phone is turned off, however it will only be + * A periodic job will be repeated until the phone is turned off, however it will only be * persisted beyond boot if the client app has declared the * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule - * periodic tasks without this permission, they simply will cease to exist after the phone + * periodic jobs without this permission, they simply will cease to exist after the phone * restarts. * Setting this function on the builder with {@link #setMinimumLatency(long)} or * {@link #setOverrideDeadline(long)} will result in an error. - * @param intervalMillis Millisecond interval for which this task will repeat. + * @param intervalMillis Millisecond interval for which this job will repeat. */ public Builder setPeriodic(long intervalMillis) { mIsPeriodic = true; @@ -359,11 +359,11 @@ public class Task implements Parcelable { } /** - * Specify that this task should be delayed by the provided amount of time. - * Because it doesn't make sense setting this property on a periodic task, doing so will + * Specify that this job should be delayed by the provided amount of time. + * Because it doesn't make sense setting this property on a periodic job, doing so will * throw an {@link java.lang.IllegalArgumentException} when - * {@link android.app.task.Task.Builder#build()} is called. - * @param minLatencyMillis Milliseconds before which this task will not be considered for + * {@link android.app.job.JobInfo.Builder#build()} is called. + * @param minLatencyMillis Milliseconds before which this job will not be considered for * execution. */ public Builder setMinimumLatency(long minLatencyMillis) { @@ -373,11 +373,11 @@ public class Task implements Parcelable { } /** - * Set deadline which is the maximum scheduling latency. The task will be run by this + * Set deadline which is the maximum scheduling latency. The job will be run by this * deadline even if other requirements are not met. Because it doesn't make sense setting - * this property on a periodic task, doing so will throw an + * this property on a periodic job, doing so will throw an * {@link java.lang.IllegalArgumentException} when - * {@link android.app.task.Task.Builder#build()} is called. + * {@link android.app.job.JobInfo.Builder#build()} is called. */ public Builder setOverrideDeadline(long maxExecutionDelayMillis) { mMaxExecutionDelayMillis = maxExecutionDelayMillis; @@ -389,13 +389,13 @@ public class Task implements Parcelable { * Set up the back-off/retry policy. * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at * 1hr. - * Note that trying to set a backoff criteria for a task with + * Note that trying to set a backoff criteria for a job with * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build(). - * This is because back-off typically does not make sense for these types of tasks. See - * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)} - * for more description of the return value for the case of a task executing while in idle + * This is because back-off typically does not make sense for these types of jobs. See + * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} + * for more description of the return value for the case of a job executing while in idle * mode. - * @param initialBackoffMillis Millisecond time interval to wait initially when task has + * @param initialBackoffMillis Millisecond time interval to wait initially when job has * failed. * @param backoffPolicy is one of {@link BackoffPolicy} */ @@ -407,25 +407,25 @@ public class Task implements Parcelable { } /** - * @return The task object to hand to the TaskManager. This object is immutable. + * @return The job object to hand to the JobScheduler. This object is immutable. */ - public Task build() { + public JobInfo build() { mExtras = new PersistableBundle(mExtras); // Make our own copy. - // Check that a deadline was not set on a periodic task. + // Check that a deadline was not set on a periodic job. if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) { throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " + - "periodic task."); + "periodic job."); } if (mIsPeriodic && (mMinLatencyMillis != 0L)) { throw new IllegalArgumentException("Can't call setMinimumLatency() on a " + - "periodic task"); + "periodic job"); } if (mBackoffPolicySet && mRequiresDeviceIdle) { - throw new IllegalArgumentException("An idle mode task will not respect any" + + throw new IllegalArgumentException("An idle mode job will not respect any" + " back-off policy, so calling setBackoffCriteria with" + " setRequiresDeviceIdle is an error."); } - return new Task(this); + return new JobInfo(this); } } diff --git a/core/java/android/app/task/TaskParams.aidl b/core/java/android/app/job/JobParameters.aidl index 9b25855..e7551b9 100644 --- a/core/java/android/app/task/TaskParams.aidl +++ b/core/java/android/app/job/JobParameters.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.app.task; +package android.app.job; -parcelable TaskParams;
\ No newline at end of file +parcelable JobParameters; diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/job/JobParameters.java index f4908c6..724856a 100644 --- a/core/java/android/app/task/TaskParams.java +++ b/core/java/android/app/job/JobParameters.java @@ -14,40 +14,42 @@ * limitations under the License */ -package android.app.task; +package android.app.job; +import android.app.job.IJobCallback; +import android.app.job.IJobCallback.Stub; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; /** - * Contains the parameters used to configure/identify your task. You do not create this object + * Contains the parameters used to configure/identify your job. You do not create this object * yourself, instead it is handed in to your application by the System. */ -public class TaskParams implements Parcelable { +public class JobParameters implements Parcelable { - private final int taskId; + private final int jobId; private final PersistableBundle extras; private final IBinder callback; /** @hide */ - public TaskParams(int taskId, PersistableBundle extras, IBinder callback) { - this.taskId = taskId; + public JobParameters(int jobId, PersistableBundle extras, IBinder callback) { + this.jobId = jobId; this.extras = extras; this.callback = callback; } /** - * @return The unique id of this task, specified at creation time. + * @return The unique id of this job, specified at creation time. */ - public int getTaskId() { - return taskId; + public int getJobId() { + return jobId; } /** - * @return The extras you passed in when constructing this task with - * {@link android.app.task.Task.Builder#setExtras(android.os.PersistableBundle)}. This will + * @return The extras you passed in when constructing this job with + * {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will * never be null. If you did not set any extras this will be an empty bundle. */ public PersistableBundle getExtras() { @@ -55,12 +57,12 @@ public class TaskParams implements Parcelable { } /** @hide */ - public ITaskCallback getCallback() { - return ITaskCallback.Stub.asInterface(callback); + public IJobCallback getCallback() { + return IJobCallback.Stub.asInterface(callback); } - private TaskParams(Parcel in) { - taskId = in.readInt(); + private JobParameters(Parcel in) { + jobId = in.readInt(); extras = in.readPersistableBundle(); callback = in.readStrongBinder(); } @@ -72,20 +74,20 @@ public class TaskParams implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(taskId); + dest.writeInt(jobId); dest.writePersistableBundle(extras); dest.writeStrongBinder(callback); } - public static final Creator<TaskParams> CREATOR = new Creator<TaskParams>() { + public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() { @Override - public TaskParams createFromParcel(Parcel in) { - return new TaskParams(in); + public JobParameters createFromParcel(Parcel in) { + return new JobParameters(in); } @Override - public TaskParams[] newArray(int size) { - return new TaskParams[size]; + public JobParameters[] newArray(int size) { + return new JobParameters[size]; } }; } diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java new file mode 100644 index 0000000..7fe192c --- /dev/null +++ b/core/java/android/app/job/JobScheduler.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.app.job; + +import java.util.List; + +import android.content.Context; + +/** + * Class for scheduling various types of jobs with the scheduling framework on the device. + * + * <p>You do not + * instantiate this class directly; instead, retrieve it through + * {@link android.content.Context#getSystemService + * Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}. + */ +public abstract class JobScheduler { + /** + * Returned from {@link #schedule(JobInfo)} when an invalid parameter was supplied. This can occur + * if the run-time for your job is too short, or perhaps the system can't resolve the + * requisite {@link JobService} in your package. + */ + public static final int RESULT_FAILURE = 0; + /** + * Returned from {@link #schedule(JobInfo)} if this application has made too many requests for + * work over too short a time. + */ + // TODO: Determine if this is necessary. + public static final int RESULT_SUCCESS = 1; + + /** + * @param job The job you wish scheduled. See + * {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs + * you can schedule. + * @return If >0, this int returns the jobId of the successfully scheduled job. + * Otherwise you have to compare the return value to the error codes defined in this class. + */ + public abstract int schedule(JobInfo job); + + /** + * Cancel a job that is pending in the JobScheduler. + * @param jobId unique identifier for this job. Obtain this value from the jobs returned by + * {@link #getAllPendingJobs()}. + * @return + */ + public abstract void cancel(int jobId); + + /** + * Cancel all jobs that have been registered with the JobScheduler by this package. + */ + public abstract void cancelAll(); + + /** + * @return a list of all the jobs registered by this package that have not yet been executed. + */ + public abstract List<JobInfo> getAllPendingJobs(); + +} diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/job/JobService.java index 8ce4484..eea0268 100644 --- a/core/java/android/app/task/TaskService.java +++ b/core/java/android/app/job/JobService.java @@ -14,9 +14,12 @@ * limitations under the License */ -package android.app.task; +package android.app.job; import android.app.Service; +import android.app.job.IJobCallback; +import android.app.job.IJobService; +import android.app.job.IJobService.Stub; import android.content.Intent; import android.os.Handler; import android.os.IBinder; @@ -28,72 +31,72 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; /** - * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p> + * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p> * <p>This is the base class that handles asynchronous requests that were previously scheduled. You - * are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where - * you will implement your task logic.</p> - * <p>This service executes each incoming task on a {@link android.os.Handler} running on your + * are responsible for overriding {@link JobService#onStartJob(JobParameters)}, which is where + * you will implement your job logic.</p> + * <p>This service executes each incoming job on a {@link android.os.Handler} running on your * application's main thread. This means that you <b>must</b> offload your execution logic to * another thread/handler/{@link android.os.AsyncTask} of your choosing. Not doing so will result - * in blocking any future callbacks from the TaskManager - specifically - * {@link #onStopTask(android.app.task.TaskParams)}, which is meant to inform you that the + * in blocking any future callbacks from the JobManager - specifically + * {@link #onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the * scheduling requirements are no longer being met.</p> */ -public abstract class TaskService extends Service { - private static final String TAG = "TaskService"; +public abstract class JobService extends Service { + private static final String TAG = "JobService"; /** - * Task services must be protected with this permission: + * Job services must be protected with this permission: * * <pre class="prettyprint"> - * <service android:name="MyTaskService" - * android:permission="android.permission.BIND_TASK_SERVICE" > + * <service android:name="MyJobService" + * android:permission="android.permission.BIND_JOB_SERVICE" > * ... * </service> * </pre> * - * <p>If a task service is declared in the manifest but not protected with this + * <p>If a job service is declared in the manifest but not protected with this * permission, that service will be ignored by the OS. */ public static final String PERMISSION_BIND = - "android.permission.BIND_TASK_SERVICE"; + "android.permission.BIND_JOB_SERVICE"; /** * Identifier for a message that will result in a call to - * {@link #onStartTask(android.app.task.TaskParams)}. + * {@link #onStartJob(android.app.job.JobParameters)}. */ - private final int MSG_EXECUTE_TASK = 0; + private final int MSG_EXECUTE_JOB = 0; /** - * Message that will result in a call to {@link #onStopTask(android.app.task.TaskParams)}. + * Message that will result in a call to {@link #onStopJob(android.app.job.JobParameters)}. */ - private final int MSG_STOP_TASK = 1; + private final int MSG_STOP_JOB = 1; /** - * Message that the client has completed execution of this task. + * Message that the client has completed execution of this job. */ - private final int MSG_TASK_FINISHED = 2; + private final int MSG_JOB_FINISHED = 2; /** Lock object for {@link #mHandler}. */ private final Object mHandlerLock = new Object(); /** - * Handler we post tasks to. Responsible for calling into the client logic, and handling the + * Handler we post jobs to. Responsible for calling into the client logic, and handling the * callback to the system. */ @GuardedBy("mHandlerLock") - TaskHandler mHandler; + JobHandler mHandler; /** Binder for this service. */ - ITaskService mBinder = new ITaskService.Stub() { + IJobService mBinder = new IJobService.Stub() { @Override - public void startTask(TaskParams taskParams) { + public void startJob(JobParameters jobParams) { ensureHandler(); - Message m = Message.obtain(mHandler, MSG_EXECUTE_TASK, taskParams); + Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams); m.sendToTarget(); } @Override - public void stopTask(TaskParams taskParams) { + public void stopJob(JobParameters jobParams) { ensureHandler(); - Message m = Message.obtain(mHandler, MSG_STOP_TASK, taskParams); + Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams); m.sendToTarget(); } }; @@ -102,7 +105,7 @@ public abstract class TaskService extends Service { void ensureHandler() { synchronized (mHandlerLock) { if (mHandler == null) { - mHandler = new TaskHandler(getMainLooper()); + mHandler = new JobHandler(getMainLooper()); } } } @@ -112,45 +115,45 @@ public abstract class TaskService extends Service { * (app-specified) mechanism. * @hide */ - class TaskHandler extends Handler { - TaskHandler(Looper looper) { + class JobHandler extends Handler { + JobHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { - final TaskParams params = (TaskParams) msg.obj; + final JobParameters params = (JobParameters) msg.obj; switch (msg.what) { - case MSG_EXECUTE_TASK: + case MSG_EXECUTE_JOB: try { - boolean workOngoing = TaskService.this.onStartTask(params); + boolean workOngoing = JobService.this.onStartJob(params); ackStartMessage(params, workOngoing); } catch (Exception e) { - Log.e(TAG, "Error while executing task: " + params.getTaskId()); + Log.e(TAG, "Error while executing job: " + params.getJobId()); throw new RuntimeException(e); } break; - case MSG_STOP_TASK: + case MSG_STOP_JOB: try { - boolean ret = TaskService.this.onStopTask(params); + boolean ret = JobService.this.onStopJob(params); ackStopMessage(params, ret); } catch (Exception e) { - Log.e(TAG, "Application unable to handle onStopTask.", e); + Log.e(TAG, "Application unable to handle onStopJob.", e); throw new RuntimeException(e); } break; - case MSG_TASK_FINISHED: + case MSG_JOB_FINISHED: final boolean needsReschedule = (msg.arg2 == 1); - ITaskCallback callback = params.getCallback(); + IJobCallback callback = params.getCallback(); if (callback != null) { try { - callback.taskFinished(params.getTaskId(), needsReschedule); + callback.jobFinished(params.getJobId(), needsReschedule); } catch (RemoteException e) { - Log.e(TAG, "Error reporting task finish to system: binder has gone" + + Log.e(TAG, "Error reporting job finish to system: binder has gone" + "away."); } } else { - Log.e(TAG, "finishTask() called for a nonexistent task id."); + Log.e(TAG, "finishJob() called for a nonexistent job id."); } break; default: @@ -159,34 +162,34 @@ public abstract class TaskService extends Service { } } - private void ackStartMessage(TaskParams params, boolean workOngoing) { - final ITaskCallback callback = params.getCallback(); - final int taskId = params.getTaskId(); + private void ackStartMessage(JobParameters params, boolean workOngoing) { + final IJobCallback callback = params.getCallback(); + final int jobId = params.getJobId(); if (callback != null) { try { - callback.acknowledgeStartMessage(taskId, workOngoing); + callback.acknowledgeStartMessage(jobId, workOngoing); } catch(RemoteException e) { - Log.e(TAG, "System unreachable for starting task."); + Log.e(TAG, "System unreachable for starting job."); } } else { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Attempting to ack a task that has already been processed."); + Log.d(TAG, "Attempting to ack a job that has already been processed."); } } } - private void ackStopMessage(TaskParams params, boolean reschedule) { - final ITaskCallback callback = params.getCallback(); - final int taskId = params.getTaskId(); + private void ackStopMessage(JobParameters params, boolean reschedule) { + final IJobCallback callback = params.getCallback(); + final int jobId = params.getJobId(); if (callback != null) { try { - callback.acknowledgeStopMessage(taskId, reschedule); + callback.acknowledgeStopMessage(jobId, reschedule); } catch(RemoteException e) { - Log.e(TAG, "System unreachable for stopping task."); + Log.e(TAG, "System unreachable for stopping job."); } } else { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Attempting to ack a task that has already been processed."); + Log.d(TAG, "Attempting to ack a job that has already been processed."); } } } @@ -198,59 +201,59 @@ public abstract class TaskService extends Service { } /** - * Override this method with the callback logic for your task. Any such logic needs to be + * Override this method with the callback logic for your job. Any such logic needs to be * performed on a separate thread, as this function is executed on your application's main * thread. * - * @param params Parameters specifying info about this task, including the extras bundle you - * optionally provided at task-creation time. + * @param params Parameters specifying info about this job, including the extras bundle you + * optionally provided at job-creation time. * @return True if your service needs to process the work (on a separate thread). False if - * there's no more work to be done for this task. + * there's no more work to be done for this job. */ - public abstract boolean onStartTask(TaskParams params); + public abstract boolean onStartJob(JobParameters params); /** - * This method is called if the system has determined that you must stop execution of your task - * even before you've had a chance to call {@link #taskFinished(TaskParams, boolean)}. + * This method is called if the system has determined that you must stop execution of your job + * even before you've had a chance to call {@link #jobFinished(JobParameters, boolean)}. * * <p>This will happen if the requirements specified at schedule time are no longer met. For * example you may have requested WiFi with - * {@link android.app.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your - * task was executing the user toggled WiFi. Another example is if you had specified - * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its + * {@link android.app.job.JobInfo.Builder#setRequiredNetworkCapabilities(int)}, yet while your + * job was executing the user toggled WiFi. Another example is if you had specified + * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its * idle maintenance window. You are solely responsible for the behaviour of your application * upon receipt of this message; your app will likely start to misbehave if you ignore it. One * immediate repercussion is that the system will cease holding a wakelock for you.</p> * - * @param params Parameters specifying info about this task. - * @return True to indicate to the TaskManager whether you'd like to reschedule this task based - * on the retry criteria provided at task creation-time. False to drop the task. Regardless of - * the value returned, your task must stop executing. + * @param params Parameters specifying info about this job. + * @return True to indicate to the JobManager whether you'd like to reschedule this job based + * on the retry criteria provided at job creation-time. False to drop the job. Regardless of + * the value returned, your job must stop executing. */ - public abstract boolean onStopTask(TaskParams params); + public abstract boolean onStopJob(JobParameters params); /** - * Callback to inform the TaskManager you've finished executing. This can be called from any + * Callback to inform the JobManager you've finished executing. This can be called from any * thread, as it will ultimately be run on your application's main thread. When the system * receives this message it will release the wakelock being held. * <p> * You can specify post-execution behaviour to the scheduler here with - * <code>needsReschedule </code>. This will apply a back-off timer to your task based on + * <code>needsReschedule </code>. This will apply a back-off timer to your job based on * the default, or what was set with - * {@link android.app.task.Task.Builder#setBackoffCriteria(long, int)}. The original - * requirements are always honoured even for a backed-off task. Note that a task running in - * idle mode will not be backed-off. Instead what will happen is the task will be re-added + * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}. The original + * requirements are always honoured even for a backed-off job. Note that a job running in + * idle mode will not be backed-off. Instead what will happen is the job will be re-added * to the queue and re-executed within a future idle maintenance window. * </p> * - * @param params Parameters specifying system-provided info about this task, this was given to - * your application in {@link #onStartTask(TaskParams)}. - * @param needsReschedule True if this task is complete, false if you want the TaskManager to + * @param params Parameters specifying system-provided info about this job, this was given to + * your application in {@link #onStartJob(JobParameters)}. + * @param needsReschedule True if this job is complete, false if you want the JobManager to * reschedule you. */ - public final void taskFinished(TaskParams params, boolean needsReschedule) { + public final void jobFinished(JobParameters params, boolean needsReschedule) { ensureHandler(); - Message m = Message.obtain(mHandler, MSG_TASK_FINISHED, params); + Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params); m.arg2 = needsReschedule ? 1 : 0; m.sendToTarget(); } diff --git a/core/java/android/app/maintenance/package.html b/core/java/android/app/maintenance/package.html new file mode 100644 index 0000000..1c9bf9d --- /dev/null +++ b/core/java/android/app/maintenance/package.html @@ -0,0 +1,5 @@ +<html> +<body> + {@hide} +</body> +</html> diff --git a/core/java/android/app/task/TaskManager.java b/core/java/android/app/task/TaskManager.java deleted file mode 100644 index 00f57da..0000000 --- a/core/java/android/app/task/TaskManager.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package android.app.task; - -import java.util.List; - -import android.content.Context; - -/** - * Class for scheduling various types of tasks with the scheduling framework on the device. - * - * <p>You do not - * instantiate this class directly; instead, retrieve it through - * {@link android.content.Context#getSystemService - * Context.getSystemService(Context.TASK_SERVICE)}. - */ -public abstract class TaskManager { - /* - * Returned from {@link #schedule(Task)} when an invalid parameter was supplied. This can occur - * if the run-time for your task is too short, or perhaps the system can't resolve the - * requisite {@link TaskService} in your package. - */ - public static final int RESULT_FAILURE = 0; - /** - * Returned from {@link #schedule(Task)} if this application has made too many requests for - * work over too short a time. - */ - // TODO: Determine if this is necessary. - public static final int RESULT_SUCCESS = 1; - - /** - * @param task The task you wish scheduled. See - * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks - * you can schedule. - * @return If >0, this int returns the taskId of the successfully scheduled task. - * Otherwise you have to compare the return value to the error codes defined in this class. - */ - public abstract int schedule(Task task); - - /** - * Cancel a task that is pending in the TaskManager. - * @param taskId unique identifier for this task. Obtain this value from the tasks returned by - * {@link #getAllPendingTasks()}. - * @return - */ - public abstract void cancel(int taskId); - - /** - * Cancel all tasks that have been registered with the TaskManager by this package. - */ - public abstract void cancelAll(); - - /** - * @return a list of all the tasks registered by this package that have not yet been executed. - */ - public abstract List<Task> getAllPendingTasks(); - -} diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index 00a0750..273d76d 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -35,8 +35,6 @@ interface IBluetoothGatt { void startScan(in int appIf, in boolean isServer); void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids); - void startScanWithUuidsScanParam(in int appIf, in boolean isServer, - in ParcelUuid[] ids, int scanWindow, int scanInterval); void startScanWithFilters(in int appIf, in boolean isServer, in ScanSettings settings, in List<ScanFilter> filters); void stopScan(in int appIf, in boolean isServer); diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl index bf9e0a7..2d8eed4 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -22,7 +22,7 @@ import android.os.ParcelUuid; * Callback definitions for interacting with BLE / GATT * @hide */ -interface IBluetoothGattCallback { +oneway interface IBluetoothGattCallback { void onClientRegistered(in int status, in int clientIf); void onClientConnectionState(in int status, in int clientIf, in boolean connected, in String address); @@ -63,7 +63,7 @@ interface IBluetoothGattCallback { in int charInstId, in ParcelUuid charUuid, in byte[] value); void onReadRemoteRssi(in String address, in int rssi, in int status); - oneway void onAdvertiseStateChange(in int advertiseState, in int status); - oneway void onMultiAdvertiseCallback(in int status); + void onAdvertiseStateChange(in int advertiseState, in int status); + void onMultiAdvertiseCallback(in int status); void onConfigureMTU(in String address, in int mtu, in int status); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2897887..ccf8451 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2059,7 +2059,7 @@ public abstract class Context { PRINT_SERVICE, MEDIA_SESSION_SERVICE, BATTERY_SERVICE, - TASK_SERVICE, + JOB_SCHEDULER_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -2116,8 +2116,8 @@ public abstract class Context { * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads * <dt> {@link #BATTERY_SERVICE} ("batterymanager") * <dd> A {@link android.os.BatteryManager} for managing battery state - * <dt> {@link #TASK_SERVICE} ("taskmanager") - * <dd> A {@link android.app.task.TaskManager} for managing scheduled tasks + * <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager") + * <dd> A {@link android.app.job.JobScheduler} for managing scheduled tasks * </dl> * * <p>Note: System services obtained via this API may be closely associated with @@ -2171,8 +2171,8 @@ public abstract class Context { * @see android.app.DownloadManager * @see #BATTERY_SERVICE * @see android.os.BatteryManager - * @see #TASK_SERVICE - * @see android.app.task.TaskManager + * @see #JOB_SCHEDULER_SERVICE + * @see android.app.job.JobScheduler */ public abstract Object getSystemService(@ServiceName @NonNull String name); @@ -2769,12 +2769,12 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link - * android.app.task.TaskManager} instance for managing occasional + * android.app.job.JobScheduler} instance for managing occasional * background tasks. * @see #getSystemService - * @see android.app.task.TaskManager + * @see android.app.job.JobScheduler */ - public static final String TASK_SERVICE = "task"; + public static final String JOB_SCHEDULER_SERVICE = "jobscheduler"; /** * Determine whether the given permission is allowed for a particular diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 04c0b9f..69fa408 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -67,7 +67,6 @@ public class LauncherApps { * * @param user The UserHandle of the profile that generated the change. * @param packageName The name of the package that was removed. - * @hide remove before ship */ void onPackageRemoved(UserHandle user, String packageName); @@ -76,7 +75,6 @@ public class LauncherApps { * * @param user The UserHandle of the profile that generated the change. * @param packageName The name of the package that was added. - * @hide remove before ship */ void onPackageAdded(UserHandle user, String packageName); @@ -85,7 +83,6 @@ public class LauncherApps { * * @param user The UserHandle of the profile that generated the change. * @param packageName The name of the package that has changed. - * @hide remove before ship */ void onPackageChanged(UserHandle user, String packageName); @@ -99,7 +96,6 @@ public class LauncherApps { * available. * @param replacing Indicates whether these packages are replacing * existing ones. - * @hide remove before ship */ void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing); @@ -113,59 +109,9 @@ public class LauncherApps { * unavailable. * @param replacing Indicates whether the packages are about to be * replaced with new versions. - * @hide remove before ship */ void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing); - /** - * Indicates that a package was removed from the specified profile. - * - * @param packageName The name of the package that was removed. - * @param user The UserHandle of the profile that generated the change. - */ - void onPackageRemoved(String packageName, UserHandle user); - - /** - * Indicates that a package was added to the specified profile. - * - * @param packageName The name of the package that was added. - * @param user The UserHandle of the profile that generated the change. - */ - void onPackageAdded(String packageName, UserHandle user); - - /** - * Indicates that a package was modified in the specified profile. - * - * @param packageName The name of the package that has changed. - * @param user The UserHandle of the profile that generated the change. - */ - void onPackageChanged(String packageName, UserHandle user); - - /** - * Indicates that one or more packages have become available. For - * example, this can happen when a removable storage card has - * reappeared. - * - * @param packageNames The names of the packages that have become - * available. - * @param user The UserHandle of the profile that generated the change. - * @param replacing Indicates whether these packages are replacing - * existing ones. - */ - void onPackagesAvailable(String [] packageNames, UserHandle user, boolean replacing); - - /** - * Indicates that one or more packages have become unavailable. For - * example, this can happen when a removable storage card has been - * removed. - * - * @param packageNames The names of the packages that have become - * unavailable. - * @param user The UserHandle of the profile that generated the change. - * @param replacing Indicates whether the packages are about to be - * replaced with new versions. - */ - void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing); } /** @hide */ @@ -361,8 +307,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackageRemoved(user, packageName); // TODO: Remove before ship - listener.onPackageRemoved(packageName, user); + listener.onPackageRemoved(user, packageName); } } } @@ -374,8 +319,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackageChanged(user, packageName); // TODO: Remove before ship - listener.onPackageChanged(packageName, user); + listener.onPackageChanged(user, packageName); } } } @@ -387,8 +331,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackageAdded(user, packageName); // TODO: Remove before ship - listener.onPackageAdded(packageName, user); + listener.onPackageAdded(user, packageName); } } } @@ -401,8 +344,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackagesAvailable(user, packageNames, replacing); // TODO: Remove - listener.onPackagesAvailable(packageNames, user, replacing); + listener.onPackagesAvailable(user, packageNames, replacing); } } } @@ -415,8 +357,7 @@ public class LauncherApps { } synchronized (LauncherApps.this) { for (OnAppsChangedListener listener : mListeners) { - listener.onPackagesUnavailable(user, packageNames, replacing); // TODO: Remove - listener.onPackagesUnavailable(packageNames, user, replacing); + listener.onPackagesUnavailable(user, packageNames, replacing); } } } diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java index 5fd0f9b..d98bdc2 100644 --- a/core/java/android/hardware/camera2/CameraCaptureSession.java +++ b/core/java/android/hardware/camera2/CameraCaptureSession.java @@ -29,19 +29,19 @@ import java.util.List; * * <p>Creating a session is an expensive operation and can take several hundred milliseconds, since * it requires configuring the camera device's internal pipelines and allocating memory buffers for - * sending images to the desired targets. While - * {@link CameraDevice#createCaptureSession createCaptureSession} will provide a - * CameraCaptureSession object immediately, configuration won't be complete until the - * {@link CameraCaptureSession.StateListener#onConfigured onConfigured} callback is called for the - * first time. If configuration cannot be completed, then the + * sending images to the desired targets. Therefore the setup is done asynchronously, and + * {@link CameraDevice#createCaptureSession createCaptureSession} will send the ready-to-use + * CameraCaptureSession to the provided listener's + * {@link CameraCaptureSession.StateListener#onConfigured onConfigured} callback. If configuration + * cannot be completed, then the * {@link CameraCaptureSession.StateListener#onConfigureFailed onConfigureFailed} is called, and the * session will not become active.</p> - * + *<!-- * <p>Any capture requests (repeating or non-repeating) submitted before the session is ready will * be queued up and will begin capture once the session becomes ready. In case the session cannot be * configured and {@link StateListener#onConfigureFailed onConfigureFailed} is called, all queued * capture requests are discarded.</p> - * + *--> * <p>If a new session is created by the camera device, then the previous session is closed, and its * associated {@link StateListener#onClosed onClosed} callback will be invoked. All * of the session methods will throw an IllegalStateException if called once the session is @@ -166,10 +166,6 @@ public abstract class CameraCaptureSession implements AutoCloseable { * capture request will be processed before any further repeating * requests are processed.<p> * - * <p>Repeating requests are a simple way for an application to maintain a - * preview or other continuous stream of frames, without having to submit - * requests through {@link #capture} at video rates.</p> - * * <p>To stop the repeating capture, call {@link #stopRepeating}. Calling * {@link #abortCaptures} will also clear the request.</p> * @@ -323,7 +319,7 @@ public abstract class CameraCaptureSession implements AutoCloseable { * * @see #setRepeatingRequest * @see #setRepeatingBurst - * @see #configureOutputs + * @see CameraDevice#createCaptureSession */ public abstract void abortCaptures() throws CameraAccessException; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 08cfc87..9eea545 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -30,7 +30,7 @@ import java.util.List; * * <p>These properties are fixed for a given CameraDevice, and can be queried * through the {@link CameraManager CameraManager} - * interface in addition to through the CameraDevice interface.</p> + * interface with {@link CameraManager#getCameraCharacteristics}.</p> * * <p>{@link CameraCharacteristics} objects are immutable.</p> * @@ -320,7 +320,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>List of frame rate ranges supported by the - * AE algorithm/hardware</p> + * auto-exposure (AE) algorithm/hardware</p> */ public static final Key<android.util.Range<Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES = new Key<android.util.Range<Integer>[]>("android.control.aeAvailableTargetFpsRanges", new TypeReference<android.util.Range<Integer>[]>() {{ }}); @@ -343,7 +343,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Rational>("android.control.aeCompensationStep", Rational.class); /** - * <p>List of AF modes that can be + * <p>List of auto-focus (AF) modes that can be * selected with {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}.</p> * <p>Not all the auto-focus modes may be supported by a * given camera device. This entry lists the valid modes for @@ -496,7 +496,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.hotPixel.availableHotPixelModes", int[].class); /** - * <p>Supported resolutions for the JPEG thumbnail</p> + * <p>Supported resolutions for the JPEG thumbnail.</p> * <p>Below condiditions will be satisfied for this size list:</p> * <ul> * <li>The sizes will be sorted by increasing pixel area (width x height). @@ -555,7 +555,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>List containing a subset of the optical image * stabilization (OIS) modes specified in * {@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}.</p> - * <p>If OIS is not implemented for a given camera device, this should + * <p>If OIS is not implemented for a given camera device, this will * contain only OFF.</p> * * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE @@ -612,7 +612,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>Direction the camera faces relative to - * device screen</p> + * device screen.</p> * @see #LENS_FACING_FRONT * @see #LENS_FACING_BACK */ @@ -622,7 +622,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>The set of noise reduction modes supported by this camera device.</p> * <p>This tag lists the valid modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}.</p> - * <p>Full-capability camera devices must laways support OFF and FAST.</p> + * <p>Full-capability camera devices must always support OFF and FAST.</p> * * @see CaptureRequest#NOISE_REDUCTION_MODE */ @@ -778,17 +778,20 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Byte>("android.request.pipelineMaxDepth", byte.class); /** - * <p>Optional. Defaults to 1. Defines how many sub-components + * <p>Defines how many sub-components * a result will be composed of.</p> * <p>In order to combat the pipeline latency, partial results * may be delivered to the application layer from the camera device as * soon as they are available.</p> - * <p>A value of 1 means that partial results are not supported.</p> - * <p>A typical use case for this might be: after requesting an AF lock the - * new AF state might be available 50% of the way through the pipeline. - * The camera device could then immediately dispatch this state via a - * partial result to the framework/application layer, and the rest of - * the metadata via later partial results.</p> + * <p>Optional; defaults to 1. A value of 1 means that partial + * results are not supported, and only the final TotalCaptureResult will + * be produced by the camera device.</p> + * <p>A typical use case for this might be: after requesting an + * auto-focus (AF) lock the new AF state might be available 50% + * of the way through the pipeline. The camera device could + * then immediately dispatch this state via a partial result to + * the application, and the rest of the metadata via later + * partial results.</p> */ public static final Key<Integer> REQUEST_PARTIAL_RESULT_COUNT = new Key<Integer>("android.request.partialResultCount", int.class); @@ -805,8 +808,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * to do this query each of android.request.availableRequestKeys, * android.request.availableResultKeys, * android.request.availableCharacteristicsKeys.</p> - * <p>XX: Maybe these should go into {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} - * as a table instead?</p> * <p>The following capabilities are guaranteed to be available on * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> FULL devices:</p> * <ul> @@ -814,14 +815,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>MANUAL_POST_PROCESSING</li> * </ul> * <p>Other capabilities may be available on either FULL or LIMITED - * devices, but the app. should query this field to be sure.</p> + * devices, but the application should query this field to be sure.</p> * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL - * @see #REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE - * @see #REQUEST_AVAILABLE_CAPABILITIES_OPTIONAL * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING - * @see #REQUEST_AVAILABLE_CAPABILITIES_ZSL * @see #REQUEST_AVAILABLE_CAPABILITIES_DNG */ public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES = @@ -837,7 +835,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * at a more granular level than capabilities. This is especially * important for optional keys that are not listed under any capability * in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p> - * <p>TODO: This should be used by #getAvailableCaptureRequestKeys.</p> * * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES * @hide @@ -862,7 +859,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * at a more granular level than capabilities. This is especially * important for optional keys that are not listed under any capability * in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p> - * <p>TODO: This should be used by #getAvailableCaptureResultKeys.</p> * * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE @@ -878,7 +874,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * android.request.availableResultKeys (except that it applies for * CameraCharacteristics instead of CaptureResult). See above for more * details.</p> - * <p>TODO: This should be used by CameraCharacteristics#getKeys.</p> * @hide */ public static final Key<int[]> REQUEST_AVAILABLE_CHARACTERISTICS_KEYS = @@ -926,10 +921,15 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<android.util.Size[]>("android.scaler.availableJpegSizes", android.util.Size[].class); /** - * <p>The maximum ratio between active area width - * and crop region width, or between active area height and - * crop region height, if the crop region height is larger - * than width</p> + * <p>The maximum ratio between both active area width + * and crop region width, and active area height and + * crop region height.</p> + * <p>This represents the maximum amount of zooming possible by + * the camera device, or equivalently, the minimum cropping + * window size.</p> + * <p>Crop regions that have a width or height that is smaller + * than this ratio allows will be rounded up to the minimum + * allowed size by the camera device.</p> */ public static final Key<Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM = new Key<Float>("android.scaler.availableMaxDigitalZoom", float.class); @@ -1326,15 +1326,21 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<android.graphics.Rect>("android.sensor.info.activeArraySize", android.graphics.Rect.class); /** - * <p>Range of valid sensitivities</p> + * <p>Range of valid sensitivities.</p> + * <p>The minimum and maximum valid values for the + * {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} control.</p> + * <p>The values are the standard ISO sensitivity values, + * as defined in ISO 12232:2006.</p> + * + * @see CaptureRequest#SENSOR_SENSITIVITY */ public static final Key<android.util.Range<Integer>> SENSOR_INFO_SENSITIVITY_RANGE = new Key<android.util.Range<Integer>>("android.sensor.info.sensitivityRange", new TypeReference<android.util.Range<Integer>>() {{ }}); /** - * <p>Arrangement of color filters on sensor; + * <p>The arrangement of color filters on sensor; * represents the colors in the top-left 2x2 section of - * the sensor, in reading order</p> + * the sensor, in reading order.</p> * @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB * @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG * @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG @@ -1372,8 +1378,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>The physical dimensions of the full pixel - * array</p> - * <p>Needed for FOV calculation for old API</p> + * array.</p> + * <p>This is the physical size of the sensor pixel + * array defined by {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}.</p> + * + * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE */ public static final Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE = new Key<android.util.SizeF>("android.sensor.info.physicalSize", android.util.SizeF.class); @@ -1381,9 +1390,17 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>Dimensions of full pixel array, possibly * including black calibration pixels.</p> - * <p>Maximum output resolution for raw format must - * match this in - * android.scaler.availableStreamConfigurations.</p> + * <p>The pixel count of the full pixel array, + * which covers {@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize} area.</p> + * <p>If a camera device supports raw sensor formats, either this + * or {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} is the maximum output + * raw size listed in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}. + * If a size corresponding to pixelArraySize is listed, the resulting + * raw sensor image will include black pixels.</p> + * + * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE */ public static final Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE = new Key<android.util.Size>("android.sensor.info.pixelArraySize", android.util.Size.class); @@ -1638,8 +1655,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Integer>("android.sensor.orientation", int.class); /** - * <p>Optional. Defaults to [OFF]. Lists the supported test - * pattern modes for {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode}.</p> + * <p>Lists the supported sensor test pattern modes for {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode}.</p> + * <p>Optional. Defaults to [OFF].</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * * @see CaptureRequest#SENSOR_TEST_PATTERN_MODE @@ -1648,10 +1665,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.sensor.availableTestPatternModes", int[].class); /** - * <p>Which face detection modes are available, - * if any</p> - * <p>OFF means face detection is disabled, it must - * be included in the list.</p> + * <p>The face detection modes that are available + * for this camera device.</p> + * <p>OFF is always supported.</p> * <p>SIMPLE means the device supports the * android.statistics.faceRectangles and * android.statistics.faceScores outputs.</p> @@ -1663,8 +1679,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.statistics.info.availableFaceDetectModes", int[].class); /** - * <p>Maximum number of simultaneously detectable - * faces</p> + * <p>The maximum number of simultaneously detectable + * faces.</p> */ public static final Key<Integer> STATISTICS_INFO_MAX_FACE_COUNT = new Key<Integer>("android.statistics.info.maxFaceCount", int.class); @@ -1673,7 +1689,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>The set of hot pixel map output modes supported by this camera device.</p> * <p>This tag lists valid output modes for {@link CaptureRequest#STATISTICS_HOT_PIXEL_MAP_MODE android.statistics.hotPixelMapMode}.</p> * <p>If no hotpixel map is available for this camera device, this will contain - * only OFF. If the hotpixel map is available, this should include both + * only OFF. If the hotpixel map is available, this will include both * the ON and OFF options.</p> * * @see CaptureRequest#STATISTICS_HOT_PIXEL_MAP_MODE diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index e9213c5..77d0c41 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -52,7 +52,7 @@ public abstract class CameraDevice implements AutoCloseable { * Create a request suitable for a camera preview window. Specifically, this * means that high frame rate is given priority over the highest-quality * post-processing. These requests would normally be used with the - * {@link #setRepeatingRequest} method. + * {@link CameraCaptureSession#setRepeatingRequest} method. * * @see #createCaptureRequest */ @@ -61,7 +61,7 @@ public abstract class CameraDevice implements AutoCloseable { /** * Create a request suitable for still image capture. Specifically, this * means prioritizing image quality over frame rate. These requests would - * commonly be used with the {@link #capture} method. + * commonly be used with the {@link CameraCaptureSession#capture} method. * * @see #createCaptureRequest */ @@ -71,7 +71,7 @@ public abstract class CameraDevice implements AutoCloseable { * Create a request suitable for video recording. Specifically, this means * that a stable frame rate is used, and post-processing is set for * recording quality. These requests would commonly be used with the - * {@link #setRepeatingRequest} method. + * {@link CameraCaptureSession#setRepeatingRequest} method. * * @see #createCaptureRequest */ @@ -81,8 +81,8 @@ public abstract class CameraDevice implements AutoCloseable { * Create a request suitable for still image capture while recording * video. Specifically, this means maximizing image quality without * disrupting the ongoing recording. These requests would commonly be used - * with the {@link #capture} method while a request based on - * {@link #TEMPLATE_RECORD} is is in use with {@link #setRepeatingRequest}. + * with the {@link CameraCaptureSession#capture} method while a request based on + * {@link #TEMPLATE_RECORD} is is in use with {@link CameraCaptureSession#setRepeatingRequest}. * * @see #createCaptureRequest */ @@ -132,116 +132,6 @@ public abstract class CameraDevice implements AutoCloseable { /** * <p>Set up a new output set of Surfaces for the camera device.</p> * - * <p>The configuration determines the set of potential output Surfaces for - * the camera device for each capture request. A given request may use all - * or a only some of the outputs. This method must be called before requests - * can be submitted to the camera with {@link #capture capture}, - * {@link #captureBurst captureBurst}, - * {@link #setRepeatingRequest setRepeatingRequest}, or - * {@link #setRepeatingBurst setRepeatingBurst}.</p> - * - * <p>Surfaces suitable for inclusion as a camera output can be created for - * various use cases and targets:</p> - * - * <ul> - * - * <li>For drawing to a {@link android.view.SurfaceView SurfaceView}: Set - * the size of the Surface with - * {@link android.view.SurfaceHolder#setFixedSize} to be one of the - * supported - * {@link StreamConfigurationMap#getOutputSizes(Class) processed sizes} - * before calling {@link android.view.SurfaceHolder#getSurface}.</li> - * - * <li>For accessing through an OpenGL texture via a - * {@link android.graphics.SurfaceTexture SurfaceTexture}: Set the size of - * the SurfaceTexture with - * {@link android.graphics.SurfaceTexture#setDefaultBufferSize} to be one - * of the supported - * {@link StreamConfigurationMap#getOutputSizes(Class) processed sizes} - * before creating a Surface from the SurfaceTexture with - * {@link Surface#Surface}.</li> - * - * <li>For recording with {@link android.media.MediaCodec}: Call - * {@link android.media.MediaCodec#createInputSurface} after configuring - * the media codec to use one of the - * {@link StreamConfigurationMap#getOutputSizes(Class) processed sizes} - * </li> - * - * <li>For recording with {@link android.media.MediaRecorder}: TODO</li> - * - * <li>For efficient YUV processing with {@link android.renderscript}: - * Create a RenderScript - * {@link android.renderscript.Allocation Allocation} with a supported YUV - * type, the IO_INPUT flag, and one of the supported - * {@link StreamConfigurationMap#getOutputSizes(int) processed sizes}. Then - * obtain the Surface with - * {@link android.renderscript.Allocation#getSurface}.</li> - * - * <li>For access to uncompressed or {@link ImageFormat#JPEG JPEG} data in the application: - * Create a {@link android.media.ImageReader} object with the desired - * {@link StreamConfigurationMap#getOutputFormats() image format}, and a size from the matching - * {@link StreamConfigurationMap#getOutputSizes(int) processed size} and {@code format}. - * Then obtain a {@link Surface} from it.</li> - * </ul> - * - * </p> - * - * <p>This function can take several hundred milliseconds to execute, since - * camera hardware may need to be powered on or reconfigured.</p> - * - * <p>The camera device will query each Surface's size and formats upon this - * call, so they must be set to a valid setting at this time (in particular: - * if the format is user-visible, it must be one of - * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of - * {@link StreamConfigurationMap#getOutputSizes(int)}).</p> - * - * <p>When this method is called with valid Surfaces, the device will transition to the {@link - * StateListener#onBusy busy state}. Once configuration is complete, the device will transition - * into the {@link StateListener#onIdle idle state}. Capture requests using the newly-configured - * Surfaces may then be submitted with {@link #capture}, {@link #captureBurst}, {@link - * #setRepeatingRequest}, or {@link #setRepeatingBurst}.</p> - * - * <p>If this method is called while the camera device is still actively processing previously - * submitted captures, then the following sequence of events occurs: The device transitions to - * the busy state and calls the {@link StateListener#onBusy} callback. Second, if a repeating - * request is set it is cleared. Third, the device finishes up all in-flight and pending - * requests. Finally, once the device is idle, it then reconfigures its outputs, and calls the - * {@link StateListener#onIdle} method once it is again ready to accept capture - * requests. Therefore, no submitted work is discarded. To idle as fast as possible, use {@link - * #flush} and wait for the idle callback before calling configureOutputs. This will discard - * work, but reaches the new configuration sooner.</p> - * - * <p>Using larger resolution outputs, or more outputs, can result in slower - * output rate from the device.</p> - * - * <p>Configuring the outputs with an empty or null list will transition the camera into an - * {@link StateListener#onUnconfigured unconfigured state} instead of the {@link - * StateListener#onIdle idle state}. </p> - * - * <p>Calling configureOutputs with the same arguments as the last call to - * configureOutputs has no effect, and the {@link StateListener#onBusy busy} - * and {@link StateListener#onIdle idle} state transitions will happen - * immediately.</p> - * - * @param outputs The new set of Surfaces that should be made available as - * targets for captured image data. - * - * @throws IllegalArgumentException if the set of output Surfaces do not - * meet the requirements - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera device is not idle, or - * if the camera device has been closed - * - * @see StateListener#onBusy - * @see StateListener#onIdle - * @see StateListener#onActive - * @see StateListener#onUnconfigured - * @see #stopRepeating - * @see #flush - * @see StreamConfigurationMap#getOutputFormats() - * @see StreamConfigurationMap#getOutputSizes(int) - * @see StreamConfigurationMap#getOutputSizes(Class) * @deprecated Use {@link #createCaptureSession} instead */ @Deprecated @@ -300,22 +190,18 @@ public abstract class CameraDevice implements AutoCloseable { * Then obtain the Surface with * {@link android.renderscript.Allocation#getSurface}.</li> * - * <li>For access to raw, uncompressed or JPEG data in the application: Create a - * {@link android.media.ImageReader} object with the one of the supported - * {@link StreamConfigurationMap#getOutputFormats() output image formats}, and a - * size from the supported - * {@link StreamConfigurationMap#getOutputSizes(int) sizes for that format}. Then obtain - * a Surface from it with {@link android.media.ImageReader#getSurface}.</li> + * <li>For access to raw, uncompressed JPEG data in the application: Create an + * {@link android.media.ImageReader} object with one of the supported output formats given by + * {@link StreamConfigurationMap#getOutputFormats()}, setting its size to one of the + * corresponding supported sizes by passing the chosen output format into + * {@link StreamConfigurationMap#getOutputSizes(int)}. Then obtain a + * {@link android.view.Surface} from it with {@link android.media.ImageReader#getSurface()}. + * </li> * * </ul> * - * </p> - * * <p>The camera device will query each Surface's size and formats upon this - * call, so they must be set to a valid setting at this time (in particular: - * if the format is user-visible, it must be one of - * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of - * {@link StreamConfigurationMap#getOutputSizes(int)}).</p> + * call, so they must be set to a valid setting at this time.</p> * * <p>It can take several hundred milliseconds for the session's configuration to complete, * since camera hardware may need to be powered on or reconfigured. Once the configuration is @@ -342,10 +228,7 @@ public abstract class CameraDevice implements AutoCloseable { * @param listener The listener to notify about the status of the new capture session. * @param handler The handler on which the listener should be invoked, or {@code null} to use * the current thread's {@link android.os.Looper looper}. - * <!-- - * @return A new camera capture session to use, or null if an empty/null set of Surfaces is - * provided. - * --> + * * @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements, * the listener is null, or the handler is null but the current * thread has no looper. @@ -393,92 +276,16 @@ public abstract class CameraDevice implements AutoCloseable { /** * <p>Submit a request for an image to be captured by this CameraDevice.</p> * - * <p>The request defines all the parameters for capturing the single image, - * including sensor, lens, flash, and post-processing settings.</p> - * - * <p>Each request will produce one {@link CaptureResult} and produce new - * frames for one or more target Surfaces, set with the CaptureRequest - * builder's {@link CaptureRequest.Builder#addTarget} method. The target - * surfaces must be configured as active outputs with - * {@link #configureOutputs} before calling this method.</p> - * - * <p>Multiple requests can be in progress at once. They are processed in - * first-in, first-out order, with minimal delays between each - * capture. Requests submitted through this method have higher priority than - * those submitted through {@link #setRepeatingRequest} or - * {@link #setRepeatingBurst}, and will be processed as soon as the current - * repeat/repeatBurst processing completes.</p> - * - * @param request the settings for this capture - * @param listener The callback object to notify once this request has been - * processed. If null, no metadata will be produced for this capture, - * although image data will still be produced. - * @param handler the handler on which the listener should be invoked, or - * {@code null} to use the current thread's {@link android.os.Looper - * looper}. - * - * @return int A unique capture sequence ID used by - * {@link CaptureListener#onCaptureSequenceCompleted}. - * - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera is currently busy or unconfigured, - * or the camera device has been closed. - * @throws IllegalArgumentException If the request targets Surfaces not - * currently configured as outputs. Or if the handler is null, the listener - * is not null, and the calling thread has no looper. - * - * @see #captureBurst - * @see #setRepeatingRequest - * @see #setRepeatingBurst - * @deprecated Use {@link CameraCaptureSession} instead + * @deprecated Use {@link CameraCaptureSession#capture} instead */ @Deprecated public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException; /** - * Submit a list of requests to be captured in sequence as a burst. The - * burst will be captured in the minimum amount of time possible, and will - * not be interleaved with requests submitted by other capture or repeat - * calls. - * - * <p>The requests will be captured in order, each capture producing one - * {@link CaptureResult} and image buffers for one or more target - * {@link android.view.Surface surfaces}. The target surfaces for each - * request (set with {@link CaptureRequest.Builder#addTarget}) must be - * configured as active outputs with {@link #configureOutputs} before - * calling this method.</p> - * - * <p>The main difference between this method and simply calling - * {@link #capture} repeatedly is that this method guarantees that no - * other requests will be interspersed with the burst.</p> - * - * @param requests the list of settings for this burst capture - * @param listener The callback object to notify each time one of the - * requests in the burst has been processed. If null, no metadata will be - * produced for any requests in this burst, although image data will still - * be produced. - * @param handler the handler on which the listener should be invoked, or - * {@code null} to use the current thread's {@link android.os.Looper - * looper}. + * Submit a list of requests to be captured in sequence as a burst. * - * @return int A unique capture sequence ID used by - * {@link CaptureListener#onCaptureSequenceCompleted}. - * - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera is currently busy or unconfigured, - * or the camera device has been closed. - * @throws IllegalArgumentException If the requests target Surfaces not - * currently configured as outputs. Or if the handler is null, the listener - * is not null, and the calling thread has no looper. Or if no requests were - * passed in. - * - * @see #capture - * @see #setRepeatingRequest - * @see #setRepeatingBurst - * @deprecated Use {@link CameraCaptureSession} instead + * @deprecated Use {@link CameraCaptureSession#captureBurst} instead */ @Deprecated public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener, @@ -487,58 +294,7 @@ public abstract class CameraDevice implements AutoCloseable { /** * Request endlessly repeating capture of images by this CameraDevice. * - * <p>With this method, the CameraDevice will continually capture images - * using the settings in the provided {@link CaptureRequest}, at the maximum - * rate possible.</p> - * - * <p>Repeating requests are a simple way for an application to maintain a - * preview or other continuous stream of frames, without having to - * continually submit identical requests through {@link #capture}.</p> - * - * <p>Repeat requests have lower priority than those submitted - * through {@link #capture} or {@link #captureBurst}, so if - * {@link #capture} is called when a repeating request is active, the - * capture request will be processed before any further repeating - * requests are processed.<p> - * - * <p>Repeating requests are a simple way for an application to maintain a - * preview or other continuous stream of frames, without having to submit - * requests through {@link #capture} at video rates.</p> - * - * <p>To stop the repeating capture, call {@link #stopRepeating}. Calling - * {@link #flush} will also clear the request.</p> - * - * <p>Calling this method will replace any earlier repeating request or - * burst set up by this method or {@link #setRepeatingBurst}, although any - * in-progress burst will be completed before the new repeat request will be - * used.</p> - * - * @param request the request to repeat indefinitely - * @param listener The callback object to notify every time the - * request finishes processing. If null, no metadata will be - * produced for this stream of requests, although image data will - * still be produced. - * @param handler the handler on which the listener should be invoked, or - * {@code null} to use the current thread's {@link android.os.Looper - * looper}. - * - * @return int A unique capture sequence ID used by - * {@link CaptureListener#onCaptureSequenceCompleted}. - * - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera is currently busy or unconfigured, - * or the camera device has been closed. - * @throws IllegalArgumentException If the requests reference Surfaces not - * currently configured as outputs. Or if the handler is null, the listener - * is not null, and the calling thread has no looper. - * - * @see #capture - * @see #captureBurst - * @see #setRepeatingBurst - * @see #stopRepeating - * @see #flush - * @deprecated Use {@link CameraCaptureSession} instead + * @deprecated Use {@link CameraCaptureSession#setRepeatingRequest} instead */ @Deprecated public abstract int setRepeatingRequest(CaptureRequest request, CaptureListener listener, @@ -548,58 +304,7 @@ public abstract class CameraDevice implements AutoCloseable { * <p>Request endlessly repeating capture of a sequence of images by this * CameraDevice.</p> * - * <p>With this method, the CameraDevice will continually capture images, - * cycling through the settings in the provided list of - * {@link CaptureRequest CaptureRequests}, at the maximum rate possible.</p> - * - * <p>If a request is submitted through {@link #capture} or - * {@link #captureBurst}, the current repetition of the request list will be - * completed before the higher-priority request is handled. This guarantees - * that the application always receives a complete repeat burst captured in - * minimal time, instead of bursts interleaved with higher-priority - * captures, or incomplete captures.</p> - * - * <p>Repeating burst requests are a simple way for an application to - * maintain a preview or other continuous stream of frames where each - * request is different in a predicatable way, without having to continually - * submit requests through {@link #captureBurst} .</p> - * - * <p>To stop the repeating capture, call {@link #stopRepeating}. Any - * ongoing burst will still be completed, however. Calling - * {@link #flush} will also clear the request.</p> - * - * <p>Calling this method will replace a previously-set repeating request or - * burst set up by this method or {@link #setRepeatingRequest}, although any - * in-progress burst will be completed before the new repeat burst will be - * used.</p> - * - * @param requests the list of requests to cycle through indefinitely - * @param listener The callback object to notify each time one of the - * requests in the repeating bursts has finished processing. If null, no - * metadata will be produced for this stream of requests, although image - * data will still be produced. - * @param handler the handler on which the listener should be invoked, or - * {@code null} to use the current thread's {@link android.os.Looper - * looper}. - * - * @return int A unique capture sequence ID used by - * {@link CaptureListener#onCaptureSequenceCompleted}. - * - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera is currently busy or unconfigured, - * or the camera device has been closed. - * @throws IllegalArgumentException If the requests reference Surfaces not - * currently configured as outputs. Or if the handler is null, the listener - * is not null, and the calling thread has no looper. Or if no requests were - * passed in. - * - * @see #capture - * @see #captureBurst - * @see #setRepeatingRequest - * @see #stopRepeating - * @see #flush - * @deprecated Use {@link CameraCaptureSession} instead + * @deprecated Use {@link CameraCaptureSession#setRepeatingBurst} instead */ @Deprecated public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, @@ -608,24 +313,9 @@ public abstract class CameraDevice implements AutoCloseable { /** * <p>Cancel any ongoing repeating capture set by either * {@link #setRepeatingRequest setRepeatingRequest} or - * {@link #setRepeatingBurst}. Has no effect on requests submitted through - * {@link #capture capture} or {@link #captureBurst captureBurst}.</p> + * {@link #setRepeatingBurst}. * - * <p>Any currently in-flight captures will still complete, as will any - * burst that is mid-capture. To ensure that the device has finished - * processing all of its capture requests and is in idle state, wait for the - * {@link StateListener#onIdle} callback after calling this - * method..</p> - * - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera is currently busy or unconfigured, - * or the camera device has been closed. - * - * @see #setRepeatingRequest - * @see #setRepeatingBurst - * @see StateListener#onIdle - * @deprecated Use {@link CameraCaptureSession} instead + * @deprecated Use {@link CameraCaptureSession#stopRepeating} instead */ @Deprecated public abstract void stopRepeating() throws CameraAccessException; @@ -634,36 +324,7 @@ public abstract class CameraDevice implements AutoCloseable { * Flush all captures currently pending and in-progress as fast as * possible. * - * <p>The camera device will discard all of its current work as fast as - * possible. Some in-flight captures may complete successfully and call - * {@link CaptureListener#onCaptureCompleted}, while others will trigger - * their {@link CaptureListener#onCaptureFailed} callbacks. If a repeating - * request or a repeating burst is set, it will be cleared by the flush.</p> - * - * <p>This method is the fastest way to idle the camera device for - * reconfiguration with {@link #configureOutputs}, at the cost of discarding - * in-progress work. Once the flush is complete, the idle callback will be - * called.</p> - * - * <p>Flushing will introduce at least a brief pause in the stream of data - * from the camera device, since once the flush is complete, the first new - * request has to make it through the entire camera pipeline before new - * output buffers are produced.</p> - * - * <p>This means that using {@code flush()} to simply remove pending - * requests is not recommended; it's best used for quickly switching output - * configurations, or for cancelling long in-progress requests (such as a - * multi-second capture).</p> - * - * @throws CameraAccessException if the camera device is no longer connected or has - * encountered a fatal error - * @throws IllegalStateException if the camera is not idle/active, - * or the camera device has been closed. - * - * @see #setRepeatingRequest - * @see #setRepeatingBurst - * @see #configureOutputs - * @deprecated Use {@link CameraCaptureSession} instead + * @deprecated Use {@link CameraCaptureSession#abortCaptures} instead */ @Deprecated public abstract void flush() throws CameraAccessException; @@ -690,15 +351,7 @@ public abstract class CameraDevice implements AutoCloseable { * <p>A listener for tracking the progress of a {@link CaptureRequest} * submitted to the camera device.</p> * - * <p>This listener is called when a request triggers a capture to start, - * and when the capture is complete. In case on an error capturing an image, - * the error method is triggered instead of the completion method.</p> - * - * @see #capture - * @see #captureBurst - * @see #setRepeatingRequest - * @see #setRepeatingBurst - * @deprecated Use {@link CameraCaptureSession} instead + * @deprecated Use {@link CameraCaptureSession.CaptureListener} instead */ @Deprecated public static abstract class CaptureListener { @@ -715,29 +368,6 @@ public abstract class CameraDevice implements AutoCloseable { * This method is called when the camera device has started capturing * the output image for the request, at the beginning of image exposure. * - * <p>This callback is invoked right as the capture of a frame begins, - * so it is the most appropriate time for playing a shutter sound, - * or triggering UI indicators of capture.</p> - * - * <p>The request that is being used for this capture is provided, along - * with the actual timestamp for the start of exposure. This timestamp - * matches the timestamp that will be included in - * {@link CaptureResult#SENSOR_TIMESTAMP the result timestamp field}, - * and in the buffers sent to each output Surface. These buffer - * timestamps are accessible through, for example, - * {@link android.media.Image#getTimestamp() Image.getTimestamp()} or - * {@link android.graphics.SurfaceTexture#getTimestamp()}.</p> - * - * <p>For the simplest way to play a shutter sound camera shutter or a - * video recording start/stop sound, see the - * {@link android.media.MediaActionSound} class.</p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera the CameraDevice sending the callback - * @param request the request for the capture that just begun - * @param timestamp the timestamp at start of capture, in nanoseconds. - * * @see android.media.MediaActionSound */ public void onCaptureStarted(CameraDevice camera, @@ -749,25 +379,6 @@ public abstract class CameraDevice implements AutoCloseable { * This method is called when some results from an image capture are * available. * - * <p>The result provided here will contain some subset of the fields of - * a full result. Multiple onCapturePartial calls may happen per - * capture; a given result field will only be present in one partial - * capture at most. The final onCaptureCompleted call will always - * contain all the fields, whether onCapturePartial was called or - * not.</p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera The CameraDevice sending the callback. - * @param request The request that was given to the CameraDevice - * @param result The partial output metadata from the capture, which - * includes a subset of the CaptureResult fields. - * - * @see #capture - * @see #captureBurst - * @see #setRepeatingRequest - * @see #setRepeatingBurst - * * @hide */ public void onCapturePartial(CameraDevice camera, @@ -779,37 +390,6 @@ public abstract class CameraDevice implements AutoCloseable { * This method is called when an image capture makes partial forward progress; some * (but not all) results from an image capture are available. * - * <p>The result provided here will contain some subset of the fields of - * a full result. Multiple {@link #onCaptureProgressed} calls may happen per - * capture; a given result field will only be present in one partial - * capture at most. The final {@link #onCaptureCompleted} call will always - * contain all the fields (in particular, the union of all the fields of all - * the partial results composing the total result).</p> - * - * <p>For each request, some result data might be available earlier than others. The typical - * delay between each partial result (per request) is a single frame interval. - * For performance-oriented use-cases, applications should query the metadata they need - * to make forward progress from the partial results and avoid waiting for the completed - * result.</p> - * - * <p>Each request will generate at least {@code 1} partial results, and at most - * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p> - * - * <p>Depending on the request settings, the number of partial results per request - * will vary, although typically the partial count could be the same as long as the - * camera device subsystems enabled stay the same.</p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera The CameraDevice sending the callback. - * @param request The request that was given to the CameraDevice - * @param partialResult The partial output metadata from the capture, which - * includes a subset of the {@link TotalCaptureResult} fields. - * - * @see #capture - * @see #captureBurst - * @see #setRepeatingRequest - * @see #setRepeatingBurst */ public void onCaptureProgressed(CameraDevice camera, CaptureRequest request, CaptureResult partialResult) { @@ -819,26 +399,6 @@ public abstract class CameraDevice implements AutoCloseable { /** * This method is called when an image capture has fully completed and all the * result metadata is available. - * - * <p>This callback will always fire after the last {@link #onCaptureProgressed}; - * in other words, no more partial results will be delivered once the completed result - * is available.</p> - * - * <p>For performance-intensive use-cases where latency is a factor, consider - * using {@link #onCaptureProgressed} instead.</p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera The CameraDevice sending the callback. - * @param request The request that was given to the CameraDevice - * @param result The total output metadata from the capture, including the - * final capture parameters and the state of the camera system during - * capture. - * - * @see #capture - * @see #captureBurst - * @see #setRepeatingRequest - * @see #setRepeatingBurst */ public void onCaptureCompleted(CameraDevice camera, CaptureRequest request, TotalCaptureResult result) { @@ -849,29 +409,6 @@ public abstract class CameraDevice implements AutoCloseable { * This method is called instead of {@link #onCaptureCompleted} when the * camera device failed to produce a {@link CaptureResult} for the * request. - * - * <p>Other requests are unaffected, and some or all image buffers from - * the capture may have been pushed to their respective output - * streams.</p> - * - * <p>Some partial results may have been delivered before the capture fails; - * however after this callback fires, no more partial results will be delivered by - * {@link #onCaptureProgressed}.</p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera - * The CameraDevice sending the callback. - * @param request - * The request that was given to the CameraDevice - * @param failure - * The output failure from the capture, including the failure reason - * and the frame number. - * - * @see #capture - * @see #captureBurst - * @see #setRepeatingRequest - * @see #setRepeatingBurst */ public void onCaptureFailed(CameraDevice camera, CaptureRequest request, CaptureFailure failure) { @@ -882,26 +419,6 @@ public abstract class CameraDevice implements AutoCloseable { * This method is called independently of the others in CaptureListener, * when a capture sequence finishes and all {@link CaptureResult} * or {@link CaptureFailure} for it have been returned via this listener. - * - * <p>In total, there will be at least one result/failure returned by this listener - * before this callback is invoked. If the capture sequence is aborted before any - * requests have been processed, {@link #onCaptureSequenceAborted} is invoked instead.</p> - * - * <p>The default implementation does nothing.</p> - * - * @param camera - * The CameraDevice sending the callback. - * @param sequenceId - * A sequence ID returned by the {@link #capture} family of functions. - * @param frameNumber - * The last frame number (returned by {@link CaptureResult#getFrameNumber} - * or {@link CaptureFailure#getFrameNumber}) in the capture sequence. - * - * @see CaptureResult#getFrameNumber() - * @see CaptureFailure#getFrameNumber() - * @see CaptureResult#getSequenceId() - * @see CaptureFailure#getSequenceId() - * @see #onCaptureSequenceAborted */ public void onCaptureSequenceCompleted(CameraDevice camera, int sequenceId, long frameNumber) { @@ -912,25 +429,6 @@ public abstract class CameraDevice implements AutoCloseable { * This method is called independently of the others in CaptureListener, * when a capture sequence aborts before any {@link CaptureResult} * or {@link CaptureFailure} for it have been returned via this listener. - * - * <p>Due to the asynchronous nature of the camera device, not all submitted captures - * are immediately processed. It is possible to clear out the pending requests - * by a variety of operations such as {@link CameraDevice#stopRepeating} or - * {@link CameraDevice#flush}. When such an event happens, - * {@link #onCaptureSequenceCompleted} will not be called.</p> - * - * <p>The default implementation does nothing.</p> - * - * @param camera - * The CameraDevice sending the callback. - * @param sequenceId - * A sequence ID returned by the {@link #capture} family of functions. - * - * @see CaptureResult#getFrameNumber() - * @see CaptureFailure#getFrameNumber() - * @see CaptureResult#getSequenceId() - * @see CaptureFailure#getSequenceId() - * @see #onCaptureSequenceCompleted */ public void onCaptureSequenceAborted(CameraDevice camera, int sequenceId) { @@ -945,14 +443,14 @@ public abstract class CameraDevice implements AutoCloseable { * <p>A listener must be provided to the {@link CameraManager#openCamera} * method to open a camera device.</p> * - * <p>These events include notifications about the device becoming idle ( - * allowing for {@link #configureOutputs} to be called), about device - * disconnection, and about unexpected device errors.</p> + * <p>These events include notifications about the device completing startup ( + * allowing for {@link #createCaptureSession} to be called), about device + * disconnection or closure, and about unexpected device errors.</p> * - * <p>Events about the progress of specific {@link CaptureRequest - * CaptureRequests} are provided through a {@link CaptureListener} given to - * the {@link #capture}, {@link #captureBurst}, {@link - * #setRepeatingRequest}, or {@link #setRepeatingBurst} methods. + * <p>Events about the progress of specific {@link CaptureRequest CaptureRequests} are provided + * through a {@link CameraCaptureSession.CaptureListener} given to the + * {@link CameraCaptureSession#capture}, {@link CameraCaptureSession#captureBurst}, + * {@link CameraCaptureSession#setRepeatingRequest}, or {@link CameraCaptureSession#setRepeatingBurst} methods. * * @see CameraManager#openCamera */ @@ -1026,8 +524,9 @@ public abstract class CameraDevice implements AutoCloseable { /** * The method called when a camera device has finished opening. * - * <p>An opened camera will immediately afterwards transition into - * {@link #onUnconfigured}.</p> + * <p>At this point, the camera device is ready to use, and + * {@link CameraDevice#createCaptureSession} can be called to set up the first capture + * session.</p> * * @param camera the camera device that has become opened */ @@ -1036,21 +535,7 @@ public abstract class CameraDevice implements AutoCloseable { /** * The method called when a camera device has no outputs configured. * - * <p>An unconfigured camera device needs to be configured with - * {@link CameraDevice#configureOutputs} before being able to - * submit any capture request.</p> - * - * <p>This state may be entered by a newly opened camera or by - * calling {@link CameraDevice#configureOutputs} with a null/empty - * list of Surfaces when idle.</p> - * - * <p>Any attempts to submit a capture request while in this state - * will result in an {@link IllegalStateException} being thrown.</p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera the camera device has that become unconfigured - * @deprecated Use {@link CameraCaptureSession.StateListener} instead. + * @deprecated Use {@link #onOpened} instead. */ @Deprecated public void onUnconfigured(CameraDevice camera) { @@ -1061,27 +546,7 @@ public abstract class CameraDevice implements AutoCloseable { * The method called when a camera device begins processing * {@link CaptureRequest capture requests}. * - * <p>A camera may not be re-configured while in this state. The camera - * will transition to the idle state once all pending captures have - * completed. If a repeating request is set, the camera will remain active - * until it is cleared and the remaining requests finish processing. To - * transition to the idle state as quickly as possible, call {@link #flush()}, - * which will idle the camera device as quickly as possible, likely canceling - * most in-progress captures.</p> - * - * <p>All calls except for {@link CameraDevice#configureOutputs} are - * legal while in this state. - * </p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera the camera device that has become active - * - * @see CameraDevice#capture - * @see CameraDevice#captureBurst - * @see CameraDevice#setRepeatingBurst - * @see CameraDevice#setRepeatingRequest - * @deprecated Use {@link CameraCaptureSession.StateListener} instead. + * @deprecated Use {@link CameraCaptureSession.StateListener#onActive} instead. */ @Deprecated public void onActive(CameraDevice camera) { @@ -1091,32 +556,7 @@ public abstract class CameraDevice implements AutoCloseable { /** * The method called when a camera device is busy. * - * <p>A camera becomes busy while it's outputs are being configured - * (after a call to {@link CameraDevice#configureOutputs} or while it's - * being flushed (after a call to {@link CameraDevice#flush}.</p> - * - * <p>Once the on-going operations are complete, the camera will automatically - * transition into {@link #onIdle} if there is at least one configured output, - * or {@link #onUnconfigured} otherwise.</p> - * - * <p>Any attempts to manipulate the camera while its is busy - * will result in an {@link IllegalStateException} being thrown.</p> - * - * <p>Only the following methods are valid to call while in this state: - * <ul> - * <li>{@link CameraDevice#getId}</li> - * <li>{@link CameraDevice#createCaptureRequest}</li> - * <li>{@link CameraDevice#close}</li> - * </ul> - * </p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera the camera device that has become busy - * - * @see CameraDevice#configureOutputs - * @see CameraDevice#flush - * @deprecated Use {@link CameraCaptureSession.StateListener} instead. + * @deprecated Use {@link CameraCaptureSession.StateListener#onConfigured} instead. */ @Deprecated public void onBusy(CameraDevice camera) { @@ -1142,30 +582,7 @@ public abstract class CameraDevice implements AutoCloseable { * The method called when a camera device has finished processing all * submitted capture requests and has reached an idle state. * - * <p>An idle camera device can have its outputs changed by calling {@link - * CameraDevice#configureOutputs}, which will transition it into the busy state.</p> - * - * <p>To idle and reconfigure outputs without canceling any submitted - * capture requests, the application needs to clear its repeating - * request/burst, if set, with {@link CameraDevice#stopRepeating}, and - * then wait for this callback to be called before calling {@link - * CameraDevice#configureOutputs}.</p> - * - * <p>To idle and reconfigure a camera device as fast as possible, the - * {@link CameraDevice#flush} method can be used, which will discard all - * pending and in-progress capture requests. Once the {@link - * CameraDevice#flush} method is called, the application must wait for - * this callback to fire before calling {@link - * CameraDevice#configureOutputs}.</p> - * - * <p>The default implementation of this method does nothing.</p> - * - * @param camera the camera device that has become idle - * - * @see CameraDevice#configureOutputs - * @see CameraDevice#stopRepeating - * @see CameraDevice#flush - * @deprecated Use {@link CameraCaptureSession.StateListener} instead. + * @deprecated Use {@link CameraCaptureSession.StateListener#onReady} instead. */ @Deprecated public void onIdle(CameraDevice camera) { @@ -1221,7 +638,7 @@ public abstract class CameraDevice implements AutoCloseable { * * @param camera The device reporting the error * @param error The error code, one of the - * {@code CameraDeviceListener.ERROR_*} values. + * {@code StateListener.ERROR_*} values. * * @see #ERROR_CAMERA_DEVICE * @see #ERROR_CAMERA_SERVICE diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 7c0f37e..0901562 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -35,7 +35,7 @@ import android.util.ArrayMap; import java.util.ArrayList; /** - * <p>An interface for iterating, listing, and connecting to + * <p>A system service manager for detecting, characterizing, and connecting to * {@link CameraDevice CameraDevices}.</p> * * <p>You can get an instance of this class by calling @@ -357,14 +357,18 @@ public final class CameraManager { } /** - * Interface for listening to camera devices becoming available or - * unavailable. + * A listener for camera devices becoming available or + * unavailable to open. * * <p>Cameras become available when they are no longer in use, or when a new * removable camera is connected. They become unavailable when some * application or service starts using a camera, or when a removable camera * is disconnected.</p> * + * <p>Extend this listener and pass an instance of the subclass to + * {@link CameraManager#addAvailabilityListener} to be notified of such availability + * changes.</p> + * * @see addAvailabilityListener */ public static abstract class AvailabilityListener { diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 94a5a79..33e1915 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -157,8 +157,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>The lens focus distance is not accurate, and the units used for - * {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} do not correspond to any physical units. - * Setting the lens to the same focus distance on separate occasions may + * {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} do not correspond to any physical units.</p> + * <p>Setting the lens to the same focus distance on separate occasions may * result in a different real focus distance, depending on factors such * as the orientation of the device, the age of the focusing mechanism, * and the device temperature. The focus distance value will still be @@ -172,20 +172,24 @@ public abstract class CameraMetadata<TKey> { public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED = 0; /** - * <p>The lens focus distance is measured in diopters. However, setting the lens - * to the same focus distance on separate occasions may result in a - * different real focus distance, depending on factors such as the - * orientation of the device, the age of the focusing mechanism, and - * the device temperature.</p> + * <p>The lens focus distance is measured in diopters.</p> + * <p>However, setting the lens to the same focus distance + * on separate occasions may result in a different real + * focus distance, depending on factors such as the + * orientation of the device, the age of the focusing + * mechanism, and the device temperature.</p> * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION */ public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE = 1; /** - * <p>The lens focus distance is measured in diopters. The lens mechanism is - * calibrated so that setting the same focus distance is repeatable on - * multiple occasions with good accuracy, and the focus distance corresponds - * to the real physical distance to the plane of best focus.</p> + * <p>The lens focus distance is measured in diopters, and + * is calibrated.</p> + * <p>The lens mechanism is calibrated so that setting the + * same focus distance is repeatable on multiple + * occasions with good accuracy, and the focus distance + * corresponds to the real physical distance to the plane + * of best focus.</p> * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION */ public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED = 2; @@ -195,11 +199,13 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>The camera device faces the same direction as the device's screen.</p> * @see CameraCharacteristics#LENS_FACING */ public static final int LENS_FACING_FRONT = 0; /** + * <p>The camera device faces the opposite direction as the device's screen.</p> * @see CameraCharacteristics#LENS_FACING */ public static final int LENS_FACING_BACK = 1; @@ -215,11 +221,10 @@ public abstract class CameraMetadata<TKey> { * <p>The full set of features supported by this capability makes * the camera2 api backwards compatible with the camera1 * (android.hardware.Camera) API.</p> - * <p>TODO: @hide this. Doesn't really mean anything except - * act as a catch-all for all the 'base' functionality.</p> * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @hide */ public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; @@ -228,15 +233,14 @@ public abstract class CameraMetadata<TKey> { * tags or functionality not encapsulated by one of the other * capabilities.</p> * <p>A typical example is all tags marked 'optional'.</p> - * <p>TODO: @hide. We may not need this if we @hide all the optional - * tags not belonging to a capability.</p> * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @hide */ public static final int REQUEST_AVAILABLE_CAPABILITIES_OPTIONAL = 1; /** * <p>The camera device can be manually controlled (3A algorithms such - * as auto exposure, and auto focus can be bypassed). + * as auto-exposure, and auto-focus can be bypassed). * The camera device supports basic manual control of the sensor image * acquisition related stages. This means the following controls are * guaranteed to be supported:</p> @@ -257,11 +261,11 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}</li> * </ul> * </li> - * <li>Manual lens control<ul> + * <li>Manual lens control (if the lens is adjustable)<ul> * <li>android.lens.*</li> * </ul> * </li> - * <li>Manual flash control<ul> + * <li>Manual flash control (if a flash unit is present)<ul> * <li>android.flash.*</li> * </ul> * </li> @@ -312,8 +316,6 @@ public abstract class CameraMetadata<TKey> { * </ul> * <p>If auto white balance is enabled, then the camera device * will accurately report the values applied by AWB in the result.</p> - * <p>The camera device will also support everything in MANUAL_SENSOR - * except manual lens control and manual flash control.</p> * <p>A given camera device may also support additional post-processing * controls, but this capability only covers the above list of controls.</p> * @@ -340,8 +342,8 @@ public abstract class CameraMetadata<TKey> { * (both input/output) will match the maximum available * resolution of JPEG streams.</li> * </ul> - * <p>@hide this, TODO: remove it when input related APIs are ready.</p> * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @hide */ public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4; @@ -355,12 +357,14 @@ public abstract class CameraMetadata<TKey> { * <li>RAW16 is reprocessable into both YUV_420_888 and JPEG * formats.</li> * <li>The maximum available resolution for RAW16 streams (both - * input/output) will match the value in - * {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}.</li> + * input/output) will match either the value in + * {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize} or + * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</li> * <li>All DNG-related optional metadata entries are provided * by the camera device.</li> * </ul> * + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES */ @@ -371,13 +375,13 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>The camera device will only support centered crop regions.</p> + * <p>The camera device only supports centered crop regions.</p> * @see CameraCharacteristics#SCALER_CROPPING_TYPE */ public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; /** - * <p>The camera device will support arbitrarily chosen crop regions.</p> + * <p>The camera device supports arbitrarily chosen crop regions.</p> * @see CameraCharacteristics#SCALER_CROPPING_TYPE */ public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; @@ -523,7 +527,7 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>android.led.transmit control is used</p> + * <p>android.led.transmit control is used.</p> * @see CameraCharacteristics#LED_AVAILABLE_LEDS * @hide */ @@ -534,11 +538,14 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>This camera device has only limited capabilities.</p> * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; /** + * <p>This camera device is capable of supporting advanced imaging + * applications.</p> * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; @@ -548,9 +555,9 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>Every frame has the requests immediately applied. - * (and furthermore for all results, - * <code>android.sync.frameNumber == android.request.frameCount</code>)</p> + * <p>Every frame has the requests immediately applied.</p> + * <p>Furthermore for all results, + * <code>android.sync.frameNumber == android.request.frameCount</code></p> * <p>Changing controls over multiple requests one after another will * produce results that have those controls applied atomically * each frame.</p> @@ -590,8 +597,8 @@ public abstract class CameraMetadata<TKey> { public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; /** - * <p>Must not slow down capture rate relative to sensor raw - * output.</p> + * <p>Color correction processing must not slow down + * capture rate relative to sensor raw output.</p> * <p>Advanced white balance adjustments above and beyond * the specified white balance pipeline may be applied.</p> * <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then @@ -604,8 +611,9 @@ public abstract class CameraMetadata<TKey> { public static final int COLOR_CORRECTION_MODE_FAST = 1; /** - * <p>Capture rate (relative to sensor raw output) - * may be reduced by high quality.</p> + * <p>Color correction processing operates at improved + * quality but reduced capture rate (relative to sensor raw + * output).</p> * <p>Advanced white balance adjustments above and beyond * the specified white balance pipeline may be applied.</p> * <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then @@ -656,8 +664,8 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>The camera device's autoexposure routine is disabled; - * the application-selected {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, + * <p>The camera device's autoexposure routine is disabled.</p> + * <p>The application-selected {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, * {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are used by the camera * device, along with android.flash.* fields, if there's @@ -672,7 +680,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>The camera device's autoexposure routine is active, - * with no flash control. The application's values for + * with no flash control.</p> + * <p>The application's values for * {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, * {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored. The @@ -689,10 +698,10 @@ public abstract class CameraMetadata<TKey> { /** * <p>Like ON, except that the camera device also controls * the camera's flash unit, firing it in low-light - * conditions. The flash may be fired during a - * precapture sequence (triggered by - * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and may be fired - * for captures for which the + * conditions.</p> + * <p>The flash may be fired during a precapture sequence + * (triggered by {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and + * may be fired for captures for which the * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} field is set to * STILL_CAPTURE</p> * @@ -705,10 +714,10 @@ public abstract class CameraMetadata<TKey> { /** * <p>Like ON, except that the camera device also controls * the camera's flash unit, always firing it for still - * captures. The flash may be fired during a precapture - * sequence (triggered by - * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and will always - * be fired for captures for which the + * captures.</p> + * <p>The flash may be fired during a precapture sequence + * (triggered by {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and + * will always be fired for captures for which the * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} field is set to * STILL_CAPTURE</p> * @@ -720,9 +729,10 @@ public abstract class CameraMetadata<TKey> { /** * <p>Like ON_AUTO_FLASH, but with automatic red eye - * reduction. If deemed necessary by the camera device, - * a red eye reduction flash will fire during the - * precapture sequence.</p> + * reduction.</p> + * <p>If deemed necessary by the camera device, a red eye + * reduction flash will fire during the precapture + * sequence.</p> * @see CaptureRequest#CONTROL_AE_MODE */ public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4; @@ -739,8 +749,9 @@ public abstract class CameraMetadata<TKey> { /** * <p>The precapture metering sequence will be started - * by the camera device. The exact effect of the precapture - * trigger depends on the current AE mode and state.</p> + * by the camera device.</p> + * <p>The exact effect of the precapture trigger depends on + * the current AE mode and state.</p> * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER */ public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; @@ -752,7 +763,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>The auto-focus routine does not control the lens; * {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} is controlled by the - * application</p> + * application.</p> * * @see CaptureRequest#LENS_FOCUS_DISTANCE * @see CaptureRequest#CONTROL_AF_MODE @@ -760,12 +771,14 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AF_MODE_OFF = 0; /** - * <p>If lens is not fixed focus.</p> - * <p>Use {@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} to determine if lens - * is fixed-focus. In this mode, the lens does not move unless + * <p>Basic automatic focus mode.</p> + * <p>In this mode, the lens does not move unless * the autofocus trigger action is called. When that trigger - * is activated, AF must transition to ACTIVE_SCAN, then to + * is activated, AF will transition to ACTIVE_SCAN, then to * the outcome of the scan (FOCUSED or NOT_FOCUSED).</p> + * <p>Always supported if lens is not fixed focus.</p> + * <p>Use {@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} to determine if lens + * is fixed-focus.</p> * <p>Triggering AF_CANCEL resets the lens position to default, * and sets the AF state to INACTIVE.</p> * @@ -775,11 +788,16 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AF_MODE_AUTO = 1; /** + * <p>Close-up focusing mode.</p> * <p>In this mode, the lens does not move unless the - * autofocus trigger action is called.</p> - * <p>When that trigger is activated, AF must transition to + * autofocus trigger action is called. When that trigger is + * activated, AF will transition to ACTIVE_SCAN, then to + * the outcome of the scan (FOCUSED or NOT_FOCUSED). This + * mode is optimized for focusing on objects very close to + * the camera.</p> + * <p>When that trigger is activated, AF will transition to * ACTIVE_SCAN, then to the outcome of the scan (FOCUSED or - * NOT_FOCUSED). Triggering cancel AF resets the lens + * NOT_FOCUSED). Triggering cancel AF resets the lens * position to default, and sets the AF state to * INACTIVE.</p> * @see CaptureRequest#CONTROL_AF_MODE @@ -830,8 +848,11 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4; /** - * <p>Extended depth of field (digital focus). AF - * trigger is ignored, AF state should always be + * <p>Extended depth of field (digital focus) mode.</p> + * <p>The camera device will produce images with an extended + * depth of field automatically; no special focusing + * operations need to be done before taking a picture.</p> + * <p>AF triggers are ignored, and the AF state will always be * INACTIVE.</p> * @see CaptureRequest#CONTROL_AF_MODE */ @@ -865,8 +886,8 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>The camera device's auto white balance routine is disabled; - * the application-selected color transform matrix + * <p>The camera device's auto-white balance routine is disabled.</p> + * <p>The application-selected color transform matrix * ({@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}) and gains * ({@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}) are used by the camera * device for manual white balance control.</p> @@ -878,9 +899,12 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AWB_MODE_OFF = 0; /** - * <p>The camera device's auto white balance routine is active; - * the application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} - * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.</p> + * <p>The camera device's auto-white balance routine is active.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> * * @see CaptureRequest#COLOR_CORRECTION_GAINS * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM @@ -889,65 +913,125 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AWB_MODE_AUTO = 1; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses incandescent light as the assumed scene - * illumination for white balance. While the exact white balance - * transforms are up to the camera device, they will approximately - * match the CIE standard illuminant A.</p> + * illumination for white balance.</p> + * <p>While the exact white balance transforms are up to the + * camera device, they will approximately match the CIE + * standard illuminant A.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_INCANDESCENT = 2; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses fluorescent light as the assumed scene - * illumination for white balance. While the exact white balance - * transforms are up to the camera device, they will approximately - * match the CIE standard illuminant F2.</p> + * illumination for white balance.</p> + * <p>While the exact white balance transforms are up to the + * camera device, they will approximately match the CIE + * standard illuminant F2.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_FLUORESCENT = 3; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses warm fluorescent light as the assumed scene - * illumination for white balance. While the exact white balance - * transforms are up to the camera device, they will approximately - * match the CIE standard illuminant F4.</p> + * illumination for white balance.</p> + * <p>While the exact white balance transforms are up to the + * camera device, they will approximately match the CIE + * standard illuminant F4.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_WARM_FLUORESCENT = 4; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses daylight light as the assumed scene - * illumination for white balance. While the exact white balance - * transforms are up to the camera device, they will approximately - * match the CIE standard illuminant D65.</p> + * illumination for white balance.</p> + * <p>While the exact white balance transforms are up to the + * camera device, they will approximately match the CIE + * standard illuminant D65.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_DAYLIGHT = 5; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses cloudy daylight light as the assumed scene * illumination for white balance.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_CLOUDY_DAYLIGHT = 6; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses twilight light as the assumed scene * illumination for white balance.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_TWILIGHT = 7; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses shade light as the assumed scene * illumination for white balance.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_SHADE = 8; @@ -957,38 +1041,43 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>This request doesn't fall into the other - * categories. Default to preview-like + * <p>The goal of this request doesn't fall into the other + * categories. The camera device will default to preview-like * behavior.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_CUSTOM = 0; /** - * <p>This request is for a preview-like usecase. The - * precapture trigger may be used to start off a metering - * w/flash sequence</p> + * <p>This request is for a preview-like use case.</p> + * <p>The precapture trigger may be used to start off a metering + * w/flash sequence.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_PREVIEW = 1; /** * <p>This request is for a still capture-type - * usecase.</p> + * use case.</p> + * <p>If the flash unit is under automatic control, it may fire as needed.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_STILL_CAPTURE = 2; /** * <p>This request is for a video recording - * usecase.</p> + * use case.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_VIDEO_RECORD = 3; /** * <p>This request is for a video snapshot (still - * image while recording video) usecase</p> + * image while recording video) use case.</p> + * <p>The camera device should take the highest-quality image + * possible (given the other settings) without disrupting the + * frame rate of video recording.<br /> + * </p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4; @@ -997,15 +1086,16 @@ public abstract class CameraMetadata<TKey> { * <p>This request is for a ZSL usecase; the * application will stream full-resolution images and * reprocess one or several later for a final - * capture</p> + * capture.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG = 5; /** * <p>This request is for manual capture use case where - * the applications want to directly control the capture parameters - * (e.g. {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} etc.).</p> + * the applications want to directly control the capture parameters.</p> + * <p>For example, the application may wish to manually control + * {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, etc.</p> * * @see CaptureRequest#SENSOR_EXPOSURE_TIME * @see CaptureRequest#SENSOR_SENSITIVITY @@ -1025,7 +1115,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>A "monocolor" effect where the image is mapped into - * a single color. This will typically be grayscale.</p> + * a single color.</p> + * <p>This will typically be grayscale.</p> * @see CaptureRequest#CONTROL_EFFECT_MODE */ public static final int CONTROL_EFFECT_MODE_MONO = 1; @@ -1085,31 +1176,42 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>Full application control of pipeline. All 3A - * routines are disabled, no other settings in - * android.control.* have any effect</p> + * <p>Full application control of pipeline.</p> + * <p>All control by the device's metering and focusing (3A) + * routines is disabled, and no other settings in + * android.control.* have any effect, except that + * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} may be used by the camera + * device to select post-processing values for processing + * blocks that do not allow for manual control, or are not + * exposed by the camera API.</p> + * <p>However, the camera device's 3A routines may continue to + * collect statistics and update their internal state so that + * when control is switched to AUTO mode, good control values + * can be immediately applied.</p> + * + * @see CaptureRequest#CONTROL_CAPTURE_INTENT * @see CaptureRequest#CONTROL_MODE */ public static final int CONTROL_MODE_OFF = 0; /** - * <p>Use settings for each individual 3A routine. - * Manual control of capture parameters is disabled. All + * <p>Use settings for each individual 3A routine.</p> + * <p>Manual control of capture parameters is disabled. All * controls in android.control.* besides sceneMode take - * effect</p> + * effect.</p> * @see CaptureRequest#CONTROL_MODE */ public static final int CONTROL_MODE_AUTO = 1; /** - * <p>Use specific scene mode. Enabling this disables - * control.aeMode, control.awbMode and control.afMode - * controls; the camera device will ignore those settings while - * USE_SCENE_MODE is active (except for FACE_PRIORITY - * scene mode). Other control entries are still active. - * This setting can only be used if scene mode is supported - * (i.e. {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes} contain some modes - * other than DISABLED).</p> + * <p>Use a specific scene mode.</p> + * <p>Enabling this disables control.aeMode, control.awbMode and + * control.afMode controls; the camera device will ignore + * those settings while USE_SCENE_MODE is active (except for + * FACE_PRIORITY scene mode). Other control entries are still + * active. This setting can only be used if scene mode is + * supported (i.e. {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes} + * contain some modes other than DISABLED).</p> * * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES * @see CaptureRequest#CONTROL_MODE @@ -1119,7 +1221,12 @@ public abstract class CameraMetadata<TKey> { /** * <p>Same as OFF mode, except that this capture will not be * used by camera device background auto-exposure, auto-white balance and - * auto-focus algorithms to update their statistics.</p> + * auto-focus algorithms (3A) to update their statistics.</p> + * <p>Specifically, the 3A routines are locked to the last + * values set from a request with AUTO, OFF, or + * USE_SCENE_MODE, and any statistics or state updates + * collected from manual captures with OFF_KEEP_STATE will be + * discarded by the camera device.</p> * @see CaptureRequest#CONTROL_MODE */ public static final int CONTROL_MODE_OFF_KEEP_STATE = 3; @@ -1137,8 +1244,9 @@ public abstract class CameraMetadata<TKey> { /** * <p>If face detection support exists, use face * detection data for auto-focus, auto-white balance, and - * auto-exposure routines. If face detection statistics are - * disabled (i.e. {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} is set to OFF), + * auto-exposure routines.</p> + * <p>If face detection statistics are disabled + * (i.e. {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} is set to OFF), * this should still operate correctly (but will not return * face detection statistics to the framework).</p> * <p>Unlike the other scene modes, {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, @@ -1154,8 +1262,8 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; /** - * <p>Optimized for photos of quickly moving objects. - * Similar to SPORTS.</p> + * <p>Optimized for photos of quickly moving objects.</p> + * <p>Similar to SPORTS.</p> * @see CaptureRequest#CONTROL_SCENE_MODE */ public static final int CONTROL_SCENE_MODE_ACTION = 2; @@ -1224,8 +1332,8 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; /** - * <p>Optimized for photos of quickly moving people. - * Similar to ACTION.</p> + * <p>Optimized for photos of quickly moving people.</p> + * <p>Similar to ACTION.</p> * @see CaptureRequest#CONTROL_SCENE_MODE */ public static final int CONTROL_SCENE_MODE_SPORTS = 13; @@ -1257,11 +1365,13 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>Video stabilization is disabled.</p> * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE */ public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0; /** + * <p>Video stabilization is enabled.</p> * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE */ public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1; @@ -1271,21 +1381,20 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>No edge enhancement is applied</p> + * <p>No edge enhancement is applied.</p> * @see CaptureRequest#EDGE_MODE */ public static final int EDGE_MODE_OFF = 0; /** - * <p>Must not slow down frame rate relative to sensor + * <p>Apply edge enhancement at a quality level that does not slow down frame rate relative to sensor * output</p> * @see CaptureRequest#EDGE_MODE */ public static final int EDGE_MODE_FAST = 1; /** - * <p>Frame rate may be reduced by high - * quality</p> + * <p>Apply high-quality edge enhancement, at a cost of reducing output frame rate.</p> * @see CaptureRequest#EDGE_MODE */ public static final int EDGE_MODE_HIGH_QUALITY = 2; @@ -1318,10 +1427,10 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>No hot pixel correction is applied.</p> * <p>The frame rate must not be reduced relative to sensor raw output * for this option.</p> - * <p>No hot pixel correction is applied. - * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> + * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP * @see CaptureRequest#HOT_PIXEL_MODE @@ -1329,10 +1438,9 @@ public abstract class CameraMetadata<TKey> { public static final int HOT_PIXEL_MODE_OFF = 0; /** - * <p>The frame rate must not be reduced relative to sensor raw output - * for this option.</p> - * <p>Hot pixel correction is applied. - * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> + * <p>Hot pixel correction is applied, without reducing frame + * rate relative to sensor raw output.</p> + * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP * @see CaptureRequest#HOT_PIXEL_MODE @@ -1340,10 +1448,9 @@ public abstract class CameraMetadata<TKey> { public static final int HOT_PIXEL_MODE_FAST = 1; /** - * <p>The frame rate may be reduced relative to sensor raw output - * for this option.</p> - * <p>A high-quality hot pixel correction is applied. - * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> + * <p>High-quality hot pixel correction is applied, at a cost + * of reducing frame rate relative to sensor raw output.</p> + * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP * @see CaptureRequest#HOT_PIXEL_MODE @@ -1371,21 +1478,21 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>No noise reduction is applied</p> + * <p>No noise reduction is applied.</p> * @see CaptureRequest#NOISE_REDUCTION_MODE */ public static final int NOISE_REDUCTION_MODE_OFF = 0; /** - * <p>Must not slow down frame rate relative to sensor - * output</p> + * <p>Noise reduction is applied without reducing frame rate relative to sensor + * output.</p> * @see CaptureRequest#NOISE_REDUCTION_MODE */ public static final int NOISE_REDUCTION_MODE_FAST = 1; /** - * <p>May slow down frame rate to provide highest - * quality</p> + * <p>High-quality noise reduction is applied, at the cost of reducing frame rate + * relative to sensor output.</p> * @see CaptureRequest#NOISE_REDUCTION_MODE */ public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; @@ -1395,8 +1502,9 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>Default. No test pattern mode is used, and the camera + * <p>No test pattern mode is used, and the camera * device returns captures from the image sensor.</p> + * <p>This is the default if the key is not set.</p> * @see CaptureRequest#SENSOR_TEST_PATTERN_MODE */ public static final int SENSOR_TEST_PATTERN_MODE_OFF = 0; @@ -1500,19 +1608,21 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>No lens shading correction is applied</p> + * <p>No lens shading correction is applied.</p> * @see CaptureRequest#SHADING_MODE */ public static final int SHADING_MODE_OFF = 0; /** - * <p>Must not slow down frame rate relative to sensor raw output</p> + * <p>Apply lens shading corrections, without slowing + * frame rate relative to sensor raw output</p> * @see CaptureRequest#SHADING_MODE */ public static final int SHADING_MODE_FAST = 1; /** - * <p>Frame rate may be reduced by high quality</p> + * <p>Apply high-quality lens shading correction, at the + * cost of reduced frame rate.</p> * @see CaptureRequest#SHADING_MODE */ public static final int SHADING_MODE_HIGH_QUALITY = 2; @@ -1522,20 +1632,28 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>Do not include face detection statistics in capture + * results.</p> * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE */ public static final int STATISTICS_FACE_DETECT_MODE_OFF = 0; /** - * <p>Optional Return rectangle and confidence - * only</p> + * <p>Return face rectangle and confidence values only.</p> + * <p>In this mode, only android.statistics.faceRectangles and + * android.statistics.faceScores outputs are valid.</p> * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE */ public static final int STATISTICS_FACE_DETECT_MODE_SIMPLE = 1; /** - * <p>Optional Return all face - * metadata</p> + * <p>Return all face + * metadata.</p> + * <p>In this mode, + * android.statistics.faceRectangles, + * android.statistics.faceScores, + * android.statistics.faceIds, and + * android.statistics.faceLandmarks outputs are valid.</p> * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE */ public static final int STATISTICS_FACE_DETECT_MODE_FULL = 2; @@ -1545,11 +1663,13 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>Do not include a lens shading map in the capture result.</p> * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE */ public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0; /** + * <p>Include a lens shading map in the capture result.</p> * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE */ public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; @@ -1573,15 +1693,15 @@ public abstract class CameraMetadata<TKey> { public static final int TONEMAP_MODE_CONTRAST_CURVE = 0; /** - * <p>Advanced gamma mapping and color enhancement may be applied.</p> - * <p>Should not slow down frame rate relative to raw sensor output.</p> + * <p>Advanced gamma mapping and color enhancement may be applied, without + * reducing frame rate compared to raw sensor output.</p> * @see CaptureRequest#TONEMAP_MODE */ public static final int TONEMAP_MODE_FAST = 1; /** - * <p>Advanced gamma mapping and color enhancement may be applied.</p> - * <p>May slow down frame rate relative to raw sensor output.</p> + * <p>High-quality gamma mapping and color enhancement will be applied, at + * the cost of reduced frame rate compared to raw sensor output.</p> * @see CaptureRequest#TONEMAP_MODE */ public static final int TONEMAP_MODE_HIGH_QUALITY = 2; @@ -1591,7 +1711,8 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>AE is off or recently reset. When a camera device is opened, it starts in + * <p>AE is off or recently reset.</p> + * <p>When a camera device is opened, it starts in * this state. This is a transient state, the camera device may skip reporting * this state in capture result.</p> * @see CaptureResult#CONTROL_AE_STATE @@ -1600,7 +1721,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>AE doesn't yet have a good set of control values - * for the current scene. This is a transient state, the camera device may skip + * for the current scene.</p> + * <p>This is a transient state, the camera device may skip * reporting this state in capture result.</p> * @see CaptureResult#CONTROL_AE_STATE */ @@ -1629,11 +1751,13 @@ public abstract class CameraMetadata<TKey> { /** * <p>AE has been asked to do a precapture sequence - * (through the {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} START), - * and is currently executing it. Once PRECAPTURE - * completes, AE will transition to CONVERGED or - * FLASH_REQUIRED as appropriate. This is a transient state, the - * camera device may skip reporting this state in capture result.</p> + * and is currently executing it.</p> + * <p>Precapture can be triggered through setting + * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} to START.</p> + * <p>Once PRECAPTURE completes, AE will transition to CONVERGED + * or FLASH_REQUIRED as appropriate. This is a transient + * state, the camera device may skip reporting this state in + * capture result.</p> * * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER * @see CaptureResult#CONTROL_AE_STATE @@ -1645,61 +1769,78 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>AF off or has not yet tried to scan/been asked - * to scan. When a camera device is opened, it starts in - * this state. This is a transient state, the camera device may - * skip reporting this state in capture result.</p> + * <p>AF is off or has not yet tried to scan/been asked + * to scan.</p> + * <p>When a camera device is opened, it starts in this + * state. This is a transient state, the camera device may + * skip reporting this state in capture + * result.</p> * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_INACTIVE = 0; /** - * <p>if CONTINUOUS_* modes are supported. AF is - * currently doing an AF scan initiated by a continuous - * autofocus mode. This is a transient state, the camera device may - * skip reporting this state in capture result.</p> + * <p>AF is currently performing an AF scan initiated the + * camera device in a continuous autofocus mode.</p> + * <p>Only used by CONTINUOUS_* AF modes. This is a transient + * state, the camera device may skip reporting this state in + * capture result.</p> * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_PASSIVE_SCAN = 1; /** - * <p>if CONTINUOUS_* modes are supported. AF currently - * believes it is in focus, but may restart scanning at - * any time. This is a transient state, the camera device may skip - * reporting this state in capture result.</p> + * <p>AF currently believes it is in focus, but may + * restart scanning at any time.</p> + * <p>Only used by CONTINUOUS_* AF modes. This is a transient + * state, the camera device may skip reporting this state in + * capture result.</p> * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_PASSIVE_FOCUSED = 2; /** - * <p>if AUTO or MACRO modes are supported. AF is doing - * an AF scan because it was triggered by AF trigger. This is a - * transient state, the camera device may skip reporting - * this state in capture result.</p> + * <p>AF is performing an AF scan because it was + * triggered by AF trigger.</p> + * <p>Only used by AUTO or MACRO AF modes. This is a transient + * state, the camera device may skip reporting this state in + * capture result.</p> * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_ACTIVE_SCAN = 3; /** - * <p>if any AF mode besides OFF is supported. AF - * believes it is focused correctly and is - * locked.</p> + * <p>AF believes it is focused correctly and has locked + * focus.</p> + * <p>This state is reached only after an explicit START AF trigger has been + * sent ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}), when good focus has been obtained.</p> + * <p>The lens will remain stationary until the AF mode ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) is changed or + * a new AF trigger is sent to the camera device ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}).</p> + * + * @see CaptureRequest#CONTROL_AF_MODE + * @see CaptureRequest#CONTROL_AF_TRIGGER * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_FOCUSED_LOCKED = 4; /** - * <p>if any AF mode besides OFF is supported. AF has - * failed to focus successfully and is - * locked.</p> + * <p>AF has failed to focus successfully and has locked + * focus.</p> + * <p>This state is reached only after an explicit START AF trigger has been + * sent ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}), when good focus cannot be obtained.</p> + * <p>The lens will remain stationary until the AF mode ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) is changed or + * a new AF trigger is sent to the camera device ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}).</p> + * + * @see CaptureRequest#CONTROL_AF_MODE + * @see CaptureRequest#CONTROL_AF_TRIGGER * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_NOT_FOCUSED_LOCKED = 5; /** - * <p>if CONTINUOUS_* modes are supported. AF finished a - * passive scan without finding focus, and may restart - * scanning at any time. This is a transient state, the camera + * <p>AF finished a passive scan without finding focus, + * and may restart scanning at any time.</p> + * <p>Only used by CONTINUOUS_* AF modes. This is a transient state, the camera * device may skip reporting this state in capture result.</p> * @see CaptureResult#CONTROL_AF_STATE */ @@ -1710,16 +1851,19 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>AWB is not in auto mode. When a camera device is opened, it - * starts in this state. This is a transient state, the camera device may - * skip reporting this state in capture result.</p> + * <p>AWB is not in auto mode, or has not yet started metering.</p> + * <p>When a camera device is opened, it starts in this + * state. This is a transient state, the camera device may + * skip reporting this state in capture + * result.</p> * @see CaptureResult#CONTROL_AWB_STATE */ public static final int CONTROL_AWB_STATE_INACTIVE = 0; /** * <p>AWB doesn't yet have a good set of control - * values for the current scene. This is a transient state, the camera device + * values for the current scene.</p> + * <p>This is a transient state, the camera device * may skip reporting this state in capture result.</p> * @see CaptureResult#CONTROL_AWB_STATE */ @@ -1767,8 +1911,9 @@ public abstract class CameraMetadata<TKey> { public static final int FLASH_STATE_FIRED = 3; /** - * <p>Flash partially illuminated this frame. This is usually due to the next - * or previous frame having the flash fire, and the flash spilling into this capture + * <p>Flash partially illuminated this frame.</p> + * <p>This is usually due to the next or previous frame having + * the flash fire, and the flash spilling into this capture * due to hardware limitations.</p> * @see CaptureResult#FLASH_STATE */ @@ -1791,8 +1936,10 @@ public abstract class CameraMetadata<TKey> { public static final int LENS_STATE_STATIONARY = 0; /** - * <p>Any of the lens parameters ({@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}, {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance}, - * {@link CaptureRequest#LENS_FILTER_DENSITY android.lens.filterDensity} or {@link CaptureRequest#LENS_APERTURE android.lens.aperture}) is changing.</p> + * <p>One or several of the lens parameters + * ({@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}, {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance}, + * {@link CaptureRequest#LENS_FILTER_DENSITY android.lens.filterDensity} or {@link CaptureRequest#LENS_APERTURE android.lens.aperture}) is + * currently changing.</p> * * @see CaptureRequest#LENS_APERTURE * @see CaptureRequest#LENS_FILTER_DENSITY @@ -1807,16 +1954,22 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>The camera device does not detect any flickering illumination + * in the current scene.</p> * @see CaptureResult#STATISTICS_SCENE_FLICKER */ public static final int STATISTICS_SCENE_FLICKER_NONE = 0; /** + * <p>The camera device detects illumination flickering at 50Hz + * in the current scene.</p> * @see CaptureResult#STATISTICS_SCENE_FLICKER */ public static final int STATISTICS_SCENE_FLICKER_50HZ = 1; /** + * <p>The camera device detects illumination flickering at 60Hz + * in the current scene.</p> * @see CaptureResult#STATISTICS_SCENE_FLICKER */ public static final int STATISTICS_SCENE_FLICKER_60HZ = 2; @@ -1826,8 +1979,8 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>The current result is not yet fully synchronized to any request. - * Synchronization is in progress, and reading metadata from this + * <p>The current result is not yet fully synchronized to any request.</p> + * <p>Synchronization is in progress, and reading metadata from this * result may include a mix of data that have taken effect since the * last synchronization time.</p> * <p>In some future result, within {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} frames, @@ -1842,10 +1995,10 @@ public abstract class CameraMetadata<TKey> { public static final int SYNC_FRAME_NUMBER_CONVERGING = -1; /** - * <p>The current result's synchronization status is unknown. The - * result may have already converged, or it may be in progress. - * Reading from this result may include some mix of settings from - * past requests.</p> + * <p>The current result's synchronization status is unknown.</p> + * <p>The result may have already converged, or it may be in + * progress. Reading from this result may include some mix + * of settings from past requests.</p> * <p>After a settings change, the new settings will eventually all * take effect for the output buffers and results. However, this * value will not change when that happens. Altering settings diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index d4dfdd5..bf7bd37 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -43,14 +43,14 @@ import java.util.Objects; * <p>CaptureRequests can be created by using a {@link Builder} instance, * obtained by calling {@link CameraDevice#createCaptureRequest}</p> * - * <p>CaptureRequests are given to {@link CameraDevice#capture} or - * {@link CameraDevice#setRepeatingRequest} to capture images from a camera.</p> + * <p>CaptureRequests are given to {@link CameraCaptureSession#capture} or + * {@link CameraCaptureSession#setRepeatingRequest} to capture images from a camera.</p> * * <p>Each request can specify a different subset of target Surfaces for the * camera to send the captured data to. All the surfaces used in a request must * be part of the surface list given to the last call to - * {@link CameraDevice#configureOutputs}, when the request is submitted to the - * camera device.</p> + * {@link CameraDevice#createCaptureSession}, when the request is submitted to the + * session.</p> * * <p>For example, a request meant for repeating preview might only include the * Surface for the preview SurfaceView or SurfaceTexture, while a @@ -357,11 +357,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * request fields to one of the templates defined in {@link CameraDevice}. * * @see CameraDevice#createCaptureRequest - * @see #TEMPLATE_PREVIEW - * @see #TEMPLATE_RECORD - * @see #TEMPLATE_STILL_CAPTURE - * @see #TEMPLATE_VIDEO_SNAPSHOT - * @see #TEMPLATE_MANUAL + * @see CameraDevice#TEMPLATE_PREVIEW + * @see CameraDevice#TEMPLATE_RECORD + * @see CameraDevice#TEMPLATE_STILL_CAPTURE + * @see CameraDevice#TEMPLATE_VIDEO_SNAPSHOT + * @see CameraDevice#TEMPLATE_MANUAL */ public final static class Builder { @@ -381,7 +381,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>Add a surface to the list of targets for this request</p> * * <p>The Surface added must be one of the surfaces included in the most - * recent call to {@link CameraDevice#configureOutputs}, when the + * recent call to {@link CameraDevice#createCaptureSession}, when the * request is given to the camera device.</p> * * <p>Adding a target more than once has no effect.</p> @@ -473,7 +473,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>The mode control selects how the image data is converted from the * sensor's native color into linear sRGB color.</p> - * <p>When auto-white balance is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this + * <p>When auto-white balance (AWB) is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this * control is overridden by the AWB routine. When AWB is disabled, the * application controls how the color mapping is performed.</p> * <p>We define the expected processing pipeline below. For consistency @@ -524,7 +524,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>A color transform matrix to use to transform - * from sensor RGB color space to output linear sRGB color space</p> + * from sensor RGB color space to output linear sRGB color space.</p> * <p>This matrix is either set by the camera device when the request * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not TRANSFORM_MATRIX, or * directly by the application in the request when the @@ -600,13 +600,17 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.control.aeAntibandingMode", int.class); /** - * <p>Adjustment to AE target image - * brightness</p> - * <p>For example, if EV step is 0.333, '6' will mean an - * exposure compensation of +2 EV; -3 will mean an exposure - * compensation of -1 EV. Note that this control will only be effective - * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when - * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p> + * <p>Adjustment to auto-exposure (AE) target image + * brightness.</p> + * <p>The adjustment is measured as a count of steps, with the + * step size defined by {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} and the + * allowed range by {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE android.control.aeCompensationRange}.</p> + * <p>For example, if the exposure value (EV) step is 0.333, '6' + * will mean an exposure compensation of +2 EV; -3 will mean an + * exposure compensation of -1 EV. One EV represents a doubling + * of image brightness. Note that this control will only be + * effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control + * will take effect even when {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p> * <p>In the event of exposure compensation value being changed, camera device * may take several frames to reach the newly requested exposure target. * During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING @@ -614,6 +618,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or * FLASH_REQUIRED (if the scene is too dark for still capture).</p> * + * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE + * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP * @see CaptureRequest#CONTROL_AE_LOCK * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureResult#CONTROL_AE_STATE @@ -622,7 +628,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.control.aeExposureCompensation", int.class); /** - * <p>Whether AE is currently locked to its latest + * <p>Whether auto-exposure (AE) is currently locked to its latest * calculated values.</p> * <p>Note that even when AE is locked, the flash may be * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH / @@ -711,9 +717,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Range over which fps can be adjusted to - * maintain exposure</p> - * <p>Only constrains AE algorithm, not manual control - * of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p> + * maintain exposure.</p> + * <p>Only constrains auto-exposure (AE) algorithm, not + * manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p> * * @see CaptureRequest#SENSOR_EXPOSURE_TIME */ @@ -727,9 +733,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * included at all in the request settings. When included and * set to START, the camera device will trigger the autoexposure * precapture metering sequence.</p> - * <p>The effect of AE precapture trigger depends on the current - * AE mode and state; see {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture - * state transition details.</p> + * <p>The precapture sequence should triggered before starting a + * high-quality still capture for final metering decisions to + * be made, and for firing pre-capture flash pulses to estimate + * scene brightness and required final capture flash power, when + * the flash is enabled.</p> + * <p>Normally, this entry should be set to START for only a + * single request, and the application should wait until the + * sequence completes before starting a new one.</p> + * <p>The exact effect of auto-exposure (AE) precapture trigger + * depends on the current AE mode and state; see + * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition + * details.</p> * * @see CaptureResult#CONTROL_AE_STATE * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE @@ -739,8 +754,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.control.aePrecaptureTrigger", int.class); /** - * <p>Whether AF is currently enabled, and what - * mode it is set to</p> + * <p>Whether auto-focus (AF) is currently enabled, and what + * mode it is set to.</p> * <p>Only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} = AUTO and the lens is not fixed focus * (i.e. <code>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} > 0</code>).</p> * <p>If the lens is controlled by the camera device auto-focus algorithm, @@ -793,7 +808,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * autofocus algorithm. If autofocus is disabled, this trigger has no effect.</p> * <p>When set to CANCEL, the camera device will cancel any active trigger, * and return to its initial AF state.</p> - * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what that means for each AF mode.</p> + * <p>Generally, applications should set this entry to START or CANCEL for only a + * single capture, and then return it to IDLE (or not set at all). Specifying + * START for multiple captures in a row means restarting the AF operation over + * and over again.</p> + * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p> * * @see CaptureResult#CONTROL_AF_STATE * @see #CONTROL_AF_TRIGGER_IDLE @@ -804,31 +823,37 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.control.afTrigger", int.class); /** - * <p>Whether AWB is currently locked to its + * <p>Whether auto-white balance (AWB) is currently locked to its * latest calculated values.</p> - * <p>Note that AWB lock is only meaningful for AUTO - * mode; in other modes, AWB is already fixed to a specific - * setting.</p> + * <p>Note that AWB lock is only meaningful when + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} is in the AUTO mode; in other modes, + * AWB is already fixed to a specific setting.</p> + * + * @see CaptureRequest#CONTROL_AWB_MODE */ public static final Key<Boolean> CONTROL_AWB_LOCK = new Key<Boolean>("android.control.awbLock", boolean.class); /** - * <p>Whether AWB is currently setting the color + * <p>Whether auto-white balance (AWB) is currently setting the color * transform fields, and what its illumination target * is.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is AUTO.</p> - * <p>When set to the ON mode, the camera device's auto white balance + * <p>When set to the ON mode, the camera device's auto-white balance * routine is enabled, overriding the application's selected * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p> - * <p>When set to the OFF mode, the camera device's auto white balance + * <p>When set to the OFF mode, the camera device's auto-white balance * routine is disabled. The application manually controls the white * balance by {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} * and {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p> - * <p>When set to any other modes, the camera device's auto white balance - * routine is disabled. The camera device uses each particular illumination - * target for white balance adjustment.</p> + * <p>When set to any other modes, the camera device's auto-white + * balance routine is disabled. The camera device uses each + * particular illumination target for white balance + * adjustment. The application's values for + * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, + * {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and + * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} are ignored.</p> * * @see CaptureRequest#COLOR_CORRECTION_GAINS * @see CaptureRequest#COLOR_CORRECTION_MODE @@ -879,8 +904,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * strategy.</p> * <p>This control (except for MANUAL) is only effective if * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> - * <p>ZERO_SHUTTER_LAG must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} - * contains ZSL. MANUAL must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} * contains MANUAL_SENSOR.</p> * * @see CaptureRequest#CONTROL_MODE @@ -955,7 +980,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>This is the mode that that is active when * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code>. Aside from FACE_PRIORITY, * these modes will disable {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, - * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.</p> + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use. + * The scene modes available for a given camera device are listed in + * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}.</p> * <p>The interpretation and implementation of these scene modes is left * to the implementor of the camera device. Their behavior will not be * consistent across all devices, and any given device may only implement @@ -963,6 +990,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AF_MODE + * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES * @see CaptureRequest#CONTROL_AWB_MODE * @see CaptureRequest#CONTROL_MODE * @see #CONTROL_SCENE_MODE_DISABLED @@ -988,7 +1016,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Whether video stabilization is - * active</p> + * active.</p> + * <p>Video stabilization automatically translates and scales images from the camera + * in order to stabilize motion between consecutive frames.</p> * <p>If enabled, video stabilization can modify the * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream * stabilized</p> @@ -1030,7 +1060,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>When set to OFF, the camera device will not fire flash for this capture.</p> * <p>When set to SINGLE, the camera device will fire flash regardless of the camera * device's auto-exposure routine's result. When used in still capture case, this - * control should be used along with AE precapture metering sequence + * control should be used along with auto-exposure (AE) precapture metering sequence * ({@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}), otherwise, the image may be incorrectly exposed.</p> * <p>When set to TORCH, the flash will be on continuously. This mode can be used * for use cases such as preview, auto-focus assist, still capture, or video recording.</p> @@ -1102,21 +1132,21 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Compression quality of the final JPEG - * image</p> - * <p>85-95 is typical usage range</p> + * image.</p> + * <p>85-95 is typical usage range.</p> */ public static final Key<Byte> JPEG_QUALITY = new Key<Byte>("android.jpeg.quality", byte.class); /** * <p>Compression quality of JPEG - * thumbnail</p> + * thumbnail.</p> */ public static final Key<Byte> JPEG_THUMBNAIL_QUALITY = new Key<Byte>("android.jpeg.thumbnailQuality", byte.class); /** - * <p>Resolution of embedded JPEG thumbnail</p> + * <p>Resolution of embedded JPEG thumbnail.</p> * <p>When set to (0, 0) value, the JPEG EXIF will not contain thumbnail, * but the captured JPEG will still be a valid image.</p> * <p>When a jpeg image capture is issued, the thumbnail size selected should have @@ -1204,7 +1234,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Distance to plane of sharpest focus, - * measured from frontmost surface of the lens</p> + * measured from frontmost surface of the lens.</p> * <p>0 means infinity focus. Used value will be clamped * to [0, {@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance}].</p> * <p>Like {@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}, this setting won't be applied @@ -1222,12 +1252,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Sets whether the camera device uses optical image stabilization (OIS) * when capturing images.</p> - * <p>OIS is used to compensate for motion blur due to small movements of - * the camera during capture. Unlike digital image stabilization, OIS makes - * use of mechanical elements to stabilize the camera sensor, and thus - * allows for longer exposure times before camera shake becomes - * apparent.</p> - * <p>This is not expected to be supported on most devices.</p> + * <p>OIS is used to compensate for motion blur due to small + * movements of the camera during capture. Unlike digital image + * stabilization ({@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode}), OIS + * makes use of mechanical elements to stabilize the camera + * sensor, and thus allows for longer exposure times before + * camera shake becomes apparent.</p> + * <p>Not all devices will support OIS; see + * {@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization} for + * available controls.</p> + * + * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE + * @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION * @see #LENS_OPTICAL_STABILIZATION_MODE_OFF * @see #LENS_OPTICAL_STABILIZATION_MODE_ON */ @@ -1235,16 +1271,15 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.lens.opticalStabilizationMode", int.class); /** - * <p>Mode of operation for the noise reduction - * algorithm</p> + * <p>Mode of operation for the noise reduction algorithm.</p> * <p>Noise filtering control. OFF means no noise reduction * will be applied by the camera device.</p> - * <p>This must be set to a valid mode in + * <p>This must be set to a valid mode from * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}.</p> * <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering * will be applied. HIGH_QUALITY mode indicates that the camera device * will use the highest-quality noise filtering algorithms, - * even if it slows down capture rate. FAST means the camera device should not + * even if it slows down capture rate. FAST means the camera device will not * slow down capture rate when applying noise filtering.</p> * * @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES @@ -1265,40 +1300,39 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.request.id", int.class); /** - * <p>(x, y, width, height).</p> - * <p>A rectangle with the top-level corner of (x,y) and size - * (width, height). The region of the sensor that is used for - * output. Each stream must use this rectangle to produce its - * output, cropping to a smaller region if necessary to - * maintain the stream's aspect ratio.</p> - * <p>HAL2.x uses only (x, y, width)</p> - * <p>The crop region is applied after the RAW to other color space (e.g. YUV) - * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage, - * it is not croppable. The crop region will be ignored by raw streams.</p> + * <p>The region of the sensor to read out for this capture.</p> + * <p>The crop region coordinate system is based off + * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the + * top-left corner of the sensor active array.</p> + * <p>Output streams use this rectangle to produce their output, + * cropping to a smaller region if necessary to maintain the + * stream's aspect ratio, then scaling the sensor input to + * match the output's configured resolution.</p> + * <p>The crop region is applied after the RAW to other color + * space (e.g. YUV) conversion. Since raw streams + * (e.g. RAW16) don't have the conversion stage, they are not + * croppable. The crop region will be ignored by raw streams.</p> * <p>For non-raw streams, any additional per-stream cropping will * be done to maximize the final pixel area of the stream.</p> * <p>For example, if the crop region is set to a 4:3 aspect - * ratio, then 4:3 streams should use the exact crop - * region. 16:9 streams should further crop vertically + * ratio, then 4:3 streams will use the exact crop + * region. 16:9 streams will further crop vertically * (letterbox).</p> * <p>Conversely, if the crop region is set to a 16:9, then 4:3 - * outputs should crop horizontally (pillarbox), and 16:9 - * streams should match exactly. These additional crops must + * outputs will crop horizontally (pillarbox), and 16:9 + * streams will match exactly. These additional crops will * be centered within the crop region.</p> - * <p>The output streams must maintain square pixels at all - * times, no matter what the relative aspect ratios of the - * crop region and the stream are. Negative values for - * corner are allowed for raw output if full pixel array is - * larger than active pixel array. Width and height may be - * rounded to nearest larger supportable width, especially - * for raw output, where only a few fixed scales may be - * possible. The width and height of the crop region cannot - * be set to be smaller than floor( activeArraySize.width / - * {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} ) and floor( - * activeArraySize.height / - * {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom}), respectively.</p> + * <p>The width and height of the crop region cannot + * be set to be smaller than + * <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and + * <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p> + * <p>The camera device may adjust the crop region to account + * for rounding and other hardware requirements; the final + * crop region used will be included in the output capture + * result.</p> * * @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE */ public static final Key<android.graphics.Rect> SCALER_CROP_REGION = new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class); @@ -1391,12 +1425,20 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Long>("android.sensor.frameDuration", long.class); /** - * <p>Gain applied to image data. Must be - * implemented through analog gain only if set to values - * below 'maximum analog sensitivity'.</p> - * <p>If the sensor can't apply this exact gain, it should lessen the - * gain to the nearest possible value (rather than gain more).</p> - * <p>ISO 12232:2006 REI method</p> + * <p>The amount of gain applied to sensor data + * before processing.</p> + * <p>The sensitivity is the standard ISO sensitivity value, + * as defined in ISO 12232:2006.</p> + * <p>The sensitivity must be within {@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}, and + * if if it less than {@link CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY android.sensor.maxAnalogSensitivity}, the camera device + * is guaranteed to use only analog amplification for applying the gain.</p> + * <p>If the camera device cannot apply the exact sensitivity + * requested, it will reduce the gain to the nearest supported + * value. The final sensitivity used will be available in the + * output capture result.</p> + * + * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE + * @see CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY */ public static final Key<Integer> SENSOR_SENSITIVITY = new Key<Integer>("android.sensor.sensitivity", int.class); @@ -1421,7 +1463,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>When enabled, the sensor sends a test pattern instead of * doing a real exposure from the camera.</p> * <p>When a test pattern is enabled, all manual sensor controls specified - * by android.sensor.* should be ignored. All other controls should + * by android.sensor.* will be ignored. All other controls should * work as normal.</p> * <p>For example, if manual flash is enabled, flash firing should still * occur (and that the test pattern remain unmodified, since the flash @@ -1459,7 +1501,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * lens shading map data in android.statistics.lensShadingMap, with size specified * by android.lens.info.shadingMapSize; the returned shading map data will be the one * applied by the camera device for this capture request.</p> - * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability + * <p>The shading map data may depend on the auto-exposure (AE) and AWB statistics, therefore the reliability * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in * AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF), * to get best results, it is recommended that the applications wait for the AE and AWB to @@ -1476,8 +1518,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.shading.mode", int.class); /** - * <p>State of the face detector - * unit</p> + * <p>Control for the face detector + * unit.</p> * <p>Whether face detection is enabled, and whether it * should output just the basic fields or the full set of * fields. Value must be one of the @@ -1494,7 +1536,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Operating mode for hotpixel map generation.</p> * <p>If set to ON, a hotpixel map is returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}. - * If set to OFF, no hotpixel map should be returned.</p> + * If set to OFF, no hotpixel map will be returned.</p> * <p>This must be set to a valid mode from {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES android.statistics.info.availableHotPixelMapModes}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP @@ -1507,7 +1549,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>Whether the camera device will output the lens * shading map in output result metadata.</p> * <p>When set to ON, - * android.statistics.lensShadingMap must be provided in + * android.statistics.lensShadingMap will be provided in * the output result metadata.</p> * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 7d07c92..3d17ed3 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -319,7 +319,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>The mode control selects how the image data is converted from the * sensor's native color into linear sRGB color.</p> - * <p>When auto-white balance is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this + * <p>When auto-white balance (AWB) is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this * control is overridden by the AWB routine. When AWB is disabled, the * application controls how the color mapping is performed.</p> * <p>We define the expected processing pipeline below. For consistency @@ -370,7 +370,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>A color transform matrix to use to transform - * from sensor RGB color space to output linear sRGB color space</p> + * from sensor RGB color space to output linear sRGB color space.</p> * <p>This matrix is either set by the camera device when the request * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not TRANSFORM_MATRIX, or * directly by the application in the request when the @@ -446,13 +446,17 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.aeAntibandingMode", int.class); /** - * <p>Adjustment to AE target image - * brightness</p> - * <p>For example, if EV step is 0.333, '6' will mean an - * exposure compensation of +2 EV; -3 will mean an exposure - * compensation of -1 EV. Note that this control will only be effective - * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when - * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p> + * <p>Adjustment to auto-exposure (AE) target image + * brightness.</p> + * <p>The adjustment is measured as a count of steps, with the + * step size defined by {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} and the + * allowed range by {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE android.control.aeCompensationRange}.</p> + * <p>For example, if the exposure value (EV) step is 0.333, '6' + * will mean an exposure compensation of +2 EV; -3 will mean an + * exposure compensation of -1 EV. One EV represents a doubling + * of image brightness. Note that this control will only be + * effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control + * will take effect even when {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p> * <p>In the event of exposure compensation value being changed, camera device * may take several frames to reach the newly requested exposure target. * During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING @@ -460,6 +464,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or * FLASH_REQUIRED (if the scene is too dark for still capture).</p> * + * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE + * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP * @see CaptureRequest#CONTROL_AE_LOCK * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureResult#CONTROL_AE_STATE @@ -468,7 +474,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.aeExposureCompensation", int.class); /** - * <p>Whether AE is currently locked to its latest + * <p>Whether auto-exposure (AE) is currently locked to its latest * calculated values.</p> * <p>Note that even when AE is locked, the flash may be * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH / @@ -557,9 +563,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Range over which fps can be adjusted to - * maintain exposure</p> - * <p>Only constrains AE algorithm, not manual control - * of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p> + * maintain exposure.</p> + * <p>Only constrains auto-exposure (AE) algorithm, not + * manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p> * * @see CaptureRequest#SENSOR_EXPOSURE_TIME */ @@ -573,9 +579,18 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * included at all in the request settings. When included and * set to START, the camera device will trigger the autoexposure * precapture metering sequence.</p> - * <p>The effect of AE precapture trigger depends on the current - * AE mode and state; see {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture - * state transition details.</p> + * <p>The precapture sequence should triggered before starting a + * high-quality still capture for final metering decisions to + * be made, and for firing pre-capture flash pulses to estimate + * scene brightness and required final capture flash power, when + * the flash is enabled.</p> + * <p>Normally, this entry should be set to START for only a + * single request, and the application should wait until the + * sequence completes before starting a new one.</p> + * <p>The exact effect of auto-exposure (AE) precapture trigger + * depends on the current AE mode and state; see + * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition + * details.</p> * * @see CaptureResult#CONTROL_AE_STATE * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE @@ -585,7 +600,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.aePrecaptureTrigger", int.class); /** - * <p>Current state of AE algorithm</p> + * <p>Current state of the auto-exposure (AE) algorithm.</p> * <p>Switching between or enabling AE modes ({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}) always * resets the AE state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode}, * or {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} if <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code> resets all @@ -783,8 +798,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.aeState", int.class); /** - * <p>Whether AF is currently enabled, and what - * mode it is set to</p> + * <p>Whether auto-focus (AF) is currently enabled, and what + * mode it is set to.</p> * <p>Only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} = AUTO and the lens is not fixed focus * (i.e. <code>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} > 0</code>).</p> * <p>If the lens is controlled by the camera device auto-focus algorithm, @@ -837,7 +852,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * autofocus algorithm. If autofocus is disabled, this trigger has no effect.</p> * <p>When set to CANCEL, the camera device will cancel any active trigger, * and return to its initial AF state.</p> - * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what that means for each AF mode.</p> + * <p>Generally, applications should set this entry to START or CANCEL for only a + * single capture, and then return it to IDLE (or not set at all). Specifying + * START for multiple captures in a row means restarting the AF operation over + * and over again.</p> + * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p> * * @see CaptureResult#CONTROL_AF_STATE * @see #CONTROL_AF_TRIGGER_IDLE @@ -848,7 +867,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.afTrigger", int.class); /** - * <p>Current state of AF algorithm.</p> + * <p>Current state of auto-focus (AF) algorithm.</p> * <p>Switching between or enabling AF modes ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) always * resets the AF state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode}, * or {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} if <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code> resets all @@ -1027,13 +1046,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <td align="center">PASSIVE_SCAN</td> * <td align="center">AF_TRIGGER</td> * <td align="center">FOCUSED_LOCKED</td> - * <td align="center">Immediate trans. If focus is good, Lens now locked</td> + * <td align="center">Immediate transition, if focus is good. Lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_SCAN</td> * <td align="center">AF_TRIGGER</td> * <td align="center">NOT_FOCUSED_LOCKED</td> - * <td align="center">Immediate trans. if focus is bad, Lens now locked</td> + * <td align="center">Immediate transition, if focus is bad. Lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_SCAN</td> @@ -1057,13 +1076,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <td align="center">PASSIVE_FOCUSED</td> * <td align="center">AF_TRIGGER</td> * <td align="center">FOCUSED_LOCKED</td> - * <td align="center">Immediate trans. Lens now locked</td> + * <td align="center">Immediate transition, lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_UNFOCUSED</td> * <td align="center">AF_TRIGGER</td> * <td align="center">NOT_FOCUSED_LOCKED</td> - * <td align="center">Immediate trans. Lens now locked</td> + * <td align="center">Immediate transition, lens now locked</td> * </tr> * <tr> * <td align="center">FOCUSED_LOCKED</td> @@ -1130,13 +1149,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <td align="center">PASSIVE_SCAN</td> * <td align="center">AF_TRIGGER</td> * <td align="center">FOCUSED_LOCKED</td> - * <td align="center">Eventual trans. once focus good, Lens now locked</td> + * <td align="center">Eventual transition once the focus is good. Lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_SCAN</td> * <td align="center">AF_TRIGGER</td> * <td align="center">NOT_FOCUSED_LOCKED</td> - * <td align="center">Eventual trans. if cannot focus, Lens now locked</td> + * <td align="center">Eventual transition if cannot find focus. Lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_SCAN</td> @@ -1245,31 +1264,37 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.afState", int.class); /** - * <p>Whether AWB is currently locked to its + * <p>Whether auto-white balance (AWB) is currently locked to its * latest calculated values.</p> - * <p>Note that AWB lock is only meaningful for AUTO - * mode; in other modes, AWB is already fixed to a specific - * setting.</p> + * <p>Note that AWB lock is only meaningful when + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} is in the AUTO mode; in other modes, + * AWB is already fixed to a specific setting.</p> + * + * @see CaptureRequest#CONTROL_AWB_MODE */ public static final Key<Boolean> CONTROL_AWB_LOCK = new Key<Boolean>("android.control.awbLock", boolean.class); /** - * <p>Whether AWB is currently setting the color + * <p>Whether auto-white balance (AWB) is currently setting the color * transform fields, and what its illumination target * is.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is AUTO.</p> - * <p>When set to the ON mode, the camera device's auto white balance + * <p>When set to the ON mode, the camera device's auto-white balance * routine is enabled, overriding the application's selected * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p> - * <p>When set to the OFF mode, the camera device's auto white balance + * <p>When set to the OFF mode, the camera device's auto-white balance * routine is disabled. The application manually controls the white * balance by {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} * and {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p> - * <p>When set to any other modes, the camera device's auto white balance - * routine is disabled. The camera device uses each particular illumination - * target for white balance adjustment.</p> + * <p>When set to any other modes, the camera device's auto-white + * balance routine is disabled. The camera device uses each + * particular illumination target for white balance + * adjustment. The application's values for + * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, + * {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and + * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} are ignored.</p> * * @see CaptureRequest#COLOR_CORRECTION_GAINS * @see CaptureRequest#COLOR_CORRECTION_MODE @@ -1320,8 +1345,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * strategy.</p> * <p>This control (except for MANUAL) is only effective if * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> - * <p>ZERO_SHUTTER_LAG must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} - * contains ZSL. MANUAL must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} * contains MANUAL_SENSOR.</p> * * @see CaptureRequest#CONTROL_MODE @@ -1338,7 +1363,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.captureIntent", int.class); /** - * <p>Current state of AWB algorithm</p> + * <p>Current state of auto-white balance (AWB) algorithm.</p> * <p>Switching between or enabling AWB modes ({@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}) always * resets the AWB state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode}, * or {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} if <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code> resets all @@ -1526,7 +1551,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>This is the mode that that is active when * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code>. Aside from FACE_PRIORITY, * these modes will disable {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, - * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.</p> + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use. + * The scene modes available for a given camera device are listed in + * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}.</p> * <p>The interpretation and implementation of these scene modes is left * to the implementor of the camera device. Their behavior will not be * consistent across all devices, and any given device may only implement @@ -1534,6 +1561,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AF_MODE + * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES * @see CaptureRequest#CONTROL_AWB_MODE * @see CaptureRequest#CONTROL_MODE * @see #CONTROL_SCENE_MODE_DISABLED @@ -1559,7 +1587,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Whether video stabilization is - * active</p> + * active.</p> + * <p>Video stabilization automatically translates and scales images from the camera + * in order to stabilize motion between consecutive frames.</p> * <p>If enabled, video stabilization can modify the * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream * stabilized</p> @@ -1601,7 +1631,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>When set to OFF, the camera device will not fire flash for this capture.</p> * <p>When set to SINGLE, the camera device will fire flash regardless of the camera * device's auto-exposure routine's result. When used in still capture case, this - * control should be used along with AE precapture metering sequence + * control should be used along with auto-exposure (AE) precapture metering sequence * ({@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}), otherwise, the image may be incorrectly exposed.</p> * <p>When set to TORCH, the flash will be on continuously. This mode can be used * for use cases such as preview, auto-focus assist, still capture, or video recording.</p> @@ -1690,21 +1720,21 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Compression quality of the final JPEG - * image</p> - * <p>85-95 is typical usage range</p> + * image.</p> + * <p>85-95 is typical usage range.</p> */ public static final Key<Byte> JPEG_QUALITY = new Key<Byte>("android.jpeg.quality", byte.class); /** * <p>Compression quality of JPEG - * thumbnail</p> + * thumbnail.</p> */ public static final Key<Byte> JPEG_THUMBNAIL_QUALITY = new Key<Byte>("android.jpeg.thumbnailQuality", byte.class); /** - * <p>Resolution of embedded JPEG thumbnail</p> + * <p>Resolution of embedded JPEG thumbnail.</p> * <p>When set to (0, 0) value, the JPEG EXIF will not contain thumbnail, * but the captured JPEG will still be a valid image.</p> * <p>When a jpeg image capture is issued, the thumbnail size selected should have @@ -1792,7 +1822,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Distance to plane of sharpest focus, - * measured from frontmost surface of the lens</p> + * measured from frontmost surface of the lens.</p> * <p>Should be zero for fixed-focus cameras</p> */ public static final Key<Float> LENS_FOCUS_DISTANCE = @@ -1800,7 +1830,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>The range of scene distances that are in - * sharp focus (depth of field)</p> + * sharp focus (depth of field).</p> * <p>If variable focus not supported, can still report * fixed depth of field range</p> */ @@ -1810,12 +1840,18 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Sets whether the camera device uses optical image stabilization (OIS) * when capturing images.</p> - * <p>OIS is used to compensate for motion blur due to small movements of - * the camera during capture. Unlike digital image stabilization, OIS makes - * use of mechanical elements to stabilize the camera sensor, and thus - * allows for longer exposure times before camera shake becomes - * apparent.</p> - * <p>This is not expected to be supported on most devices.</p> + * <p>OIS is used to compensate for motion blur due to small + * movements of the camera during capture. Unlike digital image + * stabilization ({@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode}), OIS + * makes use of mechanical elements to stabilize the camera + * sensor, and thus allows for longer exposure times before + * camera shake becomes apparent.</p> + * <p>Not all devices will support OIS; see + * {@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization} for + * available controls.</p> + * + * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE + * @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION * @see #LENS_OPTICAL_STABILIZATION_MODE_OFF * @see #LENS_OPTICAL_STABILIZATION_MODE_ON */ @@ -1859,16 +1895,15 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.lens.state", int.class); /** - * <p>Mode of operation for the noise reduction - * algorithm</p> + * <p>Mode of operation for the noise reduction algorithm.</p> * <p>Noise filtering control. OFF means no noise reduction * will be applied by the camera device.</p> - * <p>This must be set to a valid mode in + * <p>This must be set to a valid mode from * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}.</p> * <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering * will be applied. HIGH_QUALITY mode indicates that the camera device * will use the highest-quality noise filtering algorithms, - * even if it slows down capture rate. FAST means the camera device should not + * even if it slows down capture rate. FAST means the camera device will not * slow down capture rate when applying noise filtering.</p> * * @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES @@ -1934,40 +1969,39 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Byte>("android.request.pipelineDepth", byte.class); /** - * <p>(x, y, width, height).</p> - * <p>A rectangle with the top-level corner of (x,y) and size - * (width, height). The region of the sensor that is used for - * output. Each stream must use this rectangle to produce its - * output, cropping to a smaller region if necessary to - * maintain the stream's aspect ratio.</p> - * <p>HAL2.x uses only (x, y, width)</p> - * <p>The crop region is applied after the RAW to other color space (e.g. YUV) - * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage, - * it is not croppable. The crop region will be ignored by raw streams.</p> + * <p>The region of the sensor to read out for this capture.</p> + * <p>The crop region coordinate system is based off + * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the + * top-left corner of the sensor active array.</p> + * <p>Output streams use this rectangle to produce their output, + * cropping to a smaller region if necessary to maintain the + * stream's aspect ratio, then scaling the sensor input to + * match the output's configured resolution.</p> + * <p>The crop region is applied after the RAW to other color + * space (e.g. YUV) conversion. Since raw streams + * (e.g. RAW16) don't have the conversion stage, they are not + * croppable. The crop region will be ignored by raw streams.</p> * <p>For non-raw streams, any additional per-stream cropping will * be done to maximize the final pixel area of the stream.</p> * <p>For example, if the crop region is set to a 4:3 aspect - * ratio, then 4:3 streams should use the exact crop - * region. 16:9 streams should further crop vertically + * ratio, then 4:3 streams will use the exact crop + * region. 16:9 streams will further crop vertically * (letterbox).</p> * <p>Conversely, if the crop region is set to a 16:9, then 4:3 - * outputs should crop horizontally (pillarbox), and 16:9 - * streams should match exactly. These additional crops must + * outputs will crop horizontally (pillarbox), and 16:9 + * streams will match exactly. These additional crops will * be centered within the crop region.</p> - * <p>The output streams must maintain square pixels at all - * times, no matter what the relative aspect ratios of the - * crop region and the stream are. Negative values for - * corner are allowed for raw output if full pixel array is - * larger than active pixel array. Width and height may be - * rounded to nearest larger supportable width, especially - * for raw output, where only a few fixed scales may be - * possible. The width and height of the crop region cannot - * be set to be smaller than floor( activeArraySize.width / - * {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} ) and floor( - * activeArraySize.height / - * {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom}), respectively.</p> + * <p>The width and height of the crop region cannot + * be set to be smaller than + * <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and + * <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p> + * <p>The camera device may adjust the crop region to account + * for rounding and other hardware requirements; the final + * crop region used will be included in the output capture + * result.</p> * * @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE */ public static final Key<android.graphics.Rect> SCALER_CROP_REGION = new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class); @@ -2060,21 +2094,35 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Long>("android.sensor.frameDuration", long.class); /** - * <p>Gain applied to image data. Must be - * implemented through analog gain only if set to values - * below 'maximum analog sensitivity'.</p> - * <p>If the sensor can't apply this exact gain, it should lessen the - * gain to the nearest possible value (rather than gain more).</p> - * <p>ISO 12232:2006 REI method</p> + * <p>The amount of gain applied to sensor data + * before processing.</p> + * <p>The sensitivity is the standard ISO sensitivity value, + * as defined in ISO 12232:2006.</p> + * <p>The sensitivity must be within {@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}, and + * if if it less than {@link CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY android.sensor.maxAnalogSensitivity}, the camera device + * is guaranteed to use only analog amplification for applying the gain.</p> + * <p>If the camera device cannot apply the exact sensitivity + * requested, it will reduce the gain to the nearest supported + * value. The final sensitivity used will be available in the + * output capture result.</p> + * + * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE + * @see CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY */ public static final Key<Integer> SENSOR_SENSITIVITY = new Key<Integer>("android.sensor.sensitivity", int.class); /** * <p>Time at start of exposure of first - * row</p> - * <p>Monotonic, should be synced to other timestamps in - * system</p> + * row of the image sensor, in nanoseconds.</p> + * <p>The timestamps are also included in all image + * buffers produced for the same capture, and will be identical + * on all the outputs. The timestamps measure time since an + * unspecified starting point, and are monotonically + * increasing.</p> + * <p>They can be compared with the timestamps for other captures + * from the same camera device, but are not guaranteed to be + * comparable to any other time source.</p> */ public static final Key<Long> SENSOR_TIMESTAMP = new Key<Long>("android.sensor.timestamp", long.class); @@ -2150,7 +2198,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>When enabled, the sensor sends a test pattern instead of * doing a real exposure from the camera.</p> * <p>When a test pattern is enabled, all manual sensor controls specified - * by android.sensor.* should be ignored. All other controls should + * by android.sensor.* will be ignored. All other controls should * work as normal.</p> * <p>For example, if manual flash is enabled, flash firing should still * occur (and that the test pattern remain unmodified, since the flash @@ -2188,7 +2236,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * lens shading map data in android.statistics.lensShadingMap, with size specified * by android.lens.info.shadingMapSize; the returned shading map data will be the one * applied by the camera device for this capture request.</p> - * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability + * <p>The shading map data may depend on the auto-exposure (AE) and AWB statistics, therefore the reliability * of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in * AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF), * to get best results, it is recommended that the applications wait for the AE and AWB to @@ -2205,8 +2253,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.shading.mode", int.class); /** - * <p>State of the face detector - * unit</p> + * <p>Control for the face detector + * unit.</p> * <p>Whether face detection is enabled, and whether it * should output just the basic fields or the full set of * fields. Value must be one of the @@ -2221,9 +2269,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.statistics.faceDetectMode", int.class); /** - * <p>List of unique IDs for detected - * faces</p> - * <p>Only available if faceDetectMode == FULL</p> + * <p>List of unique IDs for detected faces.</p> + * <p>Each detected face is given a unique ID that is valid for as long as the face is visible + * to the camera device. A face that leaves the field of view and later returns may be + * assigned a new ID.</p> + * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL</p> + * + * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE * @hide */ public static final Key<int[]> STATISTICS_FACE_IDS = @@ -2231,8 +2283,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>List of landmarks for detected - * faces</p> - * <p>Only available if faceDetectMode == FULL</p> + * faces.</p> + * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with + * <code>(0, 0)</code> being the top-left pixel of the active array.</p> + * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL</p> + * + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE * @hide */ public static final Key<int[]> STATISTICS_FACE_LANDMARKS = @@ -2240,8 +2297,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>List of the bounding rectangles for detected - * faces</p> - * <p>Only available if faceDetectMode != OFF</p> + * faces.</p> + * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with + * <code>(0, 0)</code> being the top-left pixel of the active array.</p> + * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF</p> + * + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE * @hide */ public static final Key<android.graphics.Rect[]> STATISTICS_FACE_RECTANGLES = @@ -2250,8 +2312,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>List of the face confidence scores for * detected faces</p> - * <p>Only available if faceDetectMode != OFF. The value should be - * meaningful (for example, setting 100 at all times is illegal).</p> + * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF.</p> + * + * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE * @hide */ public static final Key<byte[]> STATISTICS_FACE_SCORES = @@ -2370,7 +2433,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * applied to that frame.</p> * <p>The 4 channel gains are defined in Bayer domain, * see {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} for details.</p> - * <p>This value should always be calculated by the AWB block, + * <p>This value should always be calculated by the auto-white balance (AWB) block, * regardless of the android.control.* current values.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @@ -2396,7 +2459,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * that frame.</p> * <p>These estimates must be provided for all frames, even if * capture settings and color transforms are set by the application.</p> - * <p>This value should always be calculated by the AWB block, + * <p>This value should always be calculated by the auto-white balance (AWB) block, * regardless of the android.control.* current values.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * @deprecated @@ -2415,12 +2478,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * The camera device uses this entry to tell the application what the scene * illuminant frequency is.</p> * <p>When manual exposure control is enabled - * (<code>{@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} == OFF</code> or <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == OFF</code>), - * the {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} doesn't do the antibanding, and the - * application can ensure it selects exposure times that do not cause banding - * issues by looking into this metadata field. See {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} - * for more details.</p> - * <p>Report NONE if there doesn't appear to be flickering illumination.</p> + * (<code>{@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} == OFF</code> or <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == + * OFF</code>), the {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} doesn't perform + * antibanding, and the application can ensure it selects + * exposure times that do not cause banding issues by looking + * into this metadata field. See + * {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} for more details.</p> + * <p>Reports NONE if there doesn't appear to be flickering illumination.</p> * * @see CaptureRequest#CONTROL_AE_ANTIBANDING_MODE * @see CaptureRequest#CONTROL_AE_MODE @@ -2435,7 +2499,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Operating mode for hotpixel map generation.</p> * <p>If set to ON, a hotpixel map is returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}. - * If set to OFF, no hotpixel map should be returned.</p> + * If set to OFF, no hotpixel map will be returned.</p> * <p>This must be set to a valid mode from {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES android.statistics.info.availableHotPixelMapModes}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP @@ -2463,7 +2527,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>Whether the camera device will output the lens * shading map in output result metadata.</p> * <p>When set to ON, - * android.statistics.lensShadingMap must be provided in + * android.statistics.lensShadingMap will be provided in * the output result metadata.</p> * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON diff --git a/core/java/android/hardware/camera2/package.html b/core/java/android/hardware/camera2/package.html index 9f6c2a9..ef0d7bd 100644 --- a/core/java/android/hardware/camera2/package.html +++ b/core/java/android/hardware/camera2/package.html @@ -34,49 +34,71 @@ framerate on most Android devices.</p> CameraDevices} provide a set of static property information that describes the hardware device and the available settings and output parameters for the device. This information is provided through the -{@link android.hardware.camera2.CameraCharacteristics} object.</p> +{@link android.hardware.camera2.CameraCharacteristics} object, and is +available through {@link +android.hardware.camera2.CameraManager#getCameraCharacteristics}</p> <p>To capture or stream images from a camera device, the application -must first configure a set of output Surfaces for use with the camera -device, with {@link -android.hardware.camera2.CameraDevice#configureOutputs}. Each -Surface has to be pre-configured with an appropriate size and format -(if applicable) to match the sizes and formats available from the -camera device. A target Surface can be obtained from a variety of -classes, including {@link android.view.SurfaceView}, {@link -android.graphics.SurfaceTexture} via {@link -android.view.Surface#Surface(SurfaceTexture), {@link -android.media.MediaCodec}, and {@link android.media.ImageReader}. +must first create a {@link +android.hardware.camera2.CameraCaptureSession camera capture session} +with a set of output Surfaces for use with the camera device, with +{@link +android.hardware.camera2.CameraDevice#createCaptureSession}. Each +Surface has to be pre-configured with an {@link +android.hardware.camera2.params.StreamConfigurationMap appropriate +size and format} (if applicable) to match the sizes and formats +available from the camera device. A target Surface can be obtained +from a variety of classes, including {@link android.view.SurfaceView}, +{@link android.graphics.SurfaceTexture} via +{@link android.view.Surface#Surface(SurfaceTexture)}, +{@link android.media.MediaCodec}, {@link android.media.MediaRecorder}, +{@link android.renderscript.Allocation}, and {@link android.media.ImageReader}. </p> +<p>Generally, camera preview images are sent to {@link +android.view.SurfaceView} or {@link android.view.TextureView} (via its +{@link android.graphics.SurfaceTexture}). Capture of JPEG images or +RAW buffers for {@link android.hardware.camera2.DngCreator} can be done +with {@link android.media.ImageReader} with the +{android.graphics.ImageFormat#JPEG} and +{android.graphics.ImageFormat#RAW_SENSOR} formats. Application-driven +processing of camera data in RenderScript, OpenGL ES, or directly in +managed or native code is best done through {@link +android.renderscript.Allocation} with a YUV {@link +android.renderscript.Type}, {@link android.graphics.SurfaceTexture}, +and {@link android.media.ImageReader} with a +{android.graphics.ImageFormat#YUV_420_888} format, respectively.</p> + <p>The application then needs to construct a {@link android.hardware.camera2.CaptureRequest}, which defines all the capture parameters needed by a camera device to capture a single image. The request also lists which of the configured output Surfaces should be used as targets for this capture. The CameraDevice has a {@link android.hardware.camera2.CameraDevice#createCaptureRequest -convenience factory method} for creating a request for a given use -case which is optimized for the Android device the application is -running on.</p> +factory method} for creating a {@link +android.hardware.camera2.CaptureRequest.Builder request builder} for a +given use case, which is optimized for the Android device the +application is running on.</p> -<p>Once the request has been set up, it can be handed to the -CameraDevice either for a one-shot {@link -android.hardware.camera2.CameraDevice#capture} or for an endlessly -{@link android.hardware.camera2.CameraDevice#setRepeatingRequest -repeating} use. Both methods also accept a list of requests to use as -a burst capture / repeating burst. Repeating requests have a lower -priority than captures, so a request submitted +<p>Once the request has been set up, it can be handed to the active +capture session either for a one-shot {@link +android.hardware.camera2.CameraCaptureSession#capture capture} or for +an endlessly {@link +android.hardware.camera2.CameraCaptureSession#setRepeatingRequest +repeating} use. Both methods also have a variant that accepts a list +of requests to use as a burst capture / repeating burst. Repeating +requests have a lower priority than captures, so a request submitted through <code>capture()</code> while there's a repeating request -configured will be captured as soon as the current repeat (burst) -capture completes.</p> +configured will be captured before any new instances of the currently +repeating (burst) capture will begin capture.</p> <p>After processing a request, the camera device will produce a {@link -android.hardware.camera2.CaptureResult} object, which contains +android.hardware.camera2.TotalCaptureResult} object, which contains information about the state of the camera device at time of capture, and the final settings used. These may vary somewhat from the request, if rounding or resolving contradictory parameters was necessary. The camera device will also send a frame of image data into each of the -output streams included in the request. These are produced +output {@code Surfaces} included in the request. These are produced asynchronously relative to the output CaptureResult, sometimes substantially later.</p> diff --git a/core/java/android/hardware/camera2/params/StreamConfiguration.java b/core/java/android/hardware/camera2/params/StreamConfiguration.java index 1c6b6e9..a6fc10f 100644 --- a/core/java/android/hardware/camera2/params/StreamConfiguration.java +++ b/core/java/android/hardware/camera2/params/StreamConfiguration.java @@ -28,8 +28,9 @@ import android.util.Size; /** * Immutable class to store the available stream - * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to be used - * when configuring streams with {@link CameraDevice#configureOutputs}. + * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to set up + * {@link android.view.Surface Surfaces} for creating a {@link CameraCaptureSession capture session} + * with {@link CameraDevice#createCaptureSession}. * <!-- TODO: link to input stream configuration --> * * <p>This is the authoritative list for all input/output formats (and sizes respectively @@ -124,7 +125,7 @@ public final class StreamConfiguration { * * @return {@code true} if output configuration, {@code false} otherwise * - * @see CameraDevice#configureOutputs + * @see CameraDevice#createCaptureSession */ public boolean isOutput() { return !mInput; diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index 4cd6d15..3036425 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -34,8 +34,10 @@ import static com.android.internal.util.Preconditions.*; /** * Immutable class to store the available stream - * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP configurations} to be used - * when configuring streams with {@link CameraDevice#configureOutputs}. + * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP configurations} to set up + * {@link android.view.Surface Surfaces} for creating a + * {@link android.hardware.camera2.CameraCaptureSession capture session} with + * {@link android.hardware.camera2.CameraDevice#createCaptureSession}. * <!-- TODO: link to input stream configuration --> * * <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively @@ -56,7 +58,7 @@ import static com.android.internal.util.Preconditions.*; * }</code></pre> * * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP - * @see CameraDevice#configureOutputs + * @see CameraDevice#createCaptureSession */ public final class StreamConfigurationMap { @@ -155,8 +157,8 @@ public final class StreamConfigurationMap { } /** - * Determine whether or not output streams can be - * {@link CameraDevice#configureOutputs configured} with a particular user-defined format. + * Determine whether or not output surfaces with a particular user-defined format can be passed + * {@link CameraDevice#createCaptureSession createCaptureSession}. * * <p>This method determines that the output {@code format} is supported by the camera device; * each output {@code surface} target may or may not itself support that {@code format}. @@ -168,7 +170,7 @@ public final class StreamConfigurationMap { * @param format an image format from either {@link ImageFormat} or {@link PixelFormat} * @return * {@code true} iff using a {@code surface} with this {@code format} will be - * supported with {@link CameraDevice#configureOutputs} + * supported with {@link CameraDevice#createCaptureSession} * * @throws IllegalArgumentException * if the image format was not a defined named constant @@ -176,7 +178,7 @@ public final class StreamConfigurationMap { * * @see ImageFormat * @see PixelFormat - * @see CameraDevice#configureOutputs + * @see CameraDevice#createCaptureSession */ public boolean isOutputSupportedFor(int format) { checkArgumentFormat(format); @@ -210,7 +212,7 @@ public final class StreamConfigurationMap { * * <p>Generally speaking this means that creating a {@link Surface} from that class <i>may</i> * provide a producer endpoint that is suitable to be used with - * {@link CameraDevice#configureOutputs}.</p> + * {@link CameraDevice#createCaptureSession}.</p> * * <p>Since not all of the above classes support output of all format and size combinations, * the particular combination should be queried with {@link #isOutputSupportedFor(Surface)}.</p> @@ -220,7 +222,7 @@ public final class StreamConfigurationMap { * * @throws NullPointerException if {@code klass} was {@code null} * - * @see CameraDevice#configureOutputs + * @see CameraDevice#createCaptureSession * @see #isOutputSupportedFor(Surface) */ public static <T> boolean isOutputSupportedFor(Class<T> klass) { @@ -244,8 +246,8 @@ public final class StreamConfigurationMap { } /** - * Determine whether or not the {@code surface} in its current state is suitable to be - * {@link CameraDevice#configureOutputs configured} as an output. + * Determine whether or not the {@code surface} in its current state is suitable to be included + * in a {@link CameraDevice#createCaptureSession capture session} as an output. * * <p>Not all surfaces are usable with the {@link CameraDevice}, and not all configurations * of that {@code surface} are compatible. Some classes that provide the {@code surface} are @@ -269,7 +271,7 @@ public final class StreamConfigurationMap { * * @throws NullPointerException if {@code surface} was {@code null} * - * @see CameraDevice#configureOutputs + * @see CameraDevice#createCaptureSession * @see #isOutputSupportedFor(Class) */ public boolean isOutputSupportedFor(Surface surface) { diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.aidl b/core/java/android/hardware/hdmi/HdmiPortInfo.aidl new file mode 100644 index 0000000..157b5b3 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiPortInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.hdmi; + +parcelable HdmiPortInfo; diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java new file mode 100644 index 0000000..7b25f8a --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.hdmi; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class to encapsulate HDMI port information. Contains the capability of the ports such as + * HDMI-CEC, MHL, ARC(Audio Return Channel), and physical address assigned to each port. + * + * @hide + */ +@SystemApi +public final class HdmiPortInfo implements Parcelable { + /** HDMI port type: Input */ + public static final int PORT_INPUT = 0; + + /** HDMI port type: Output */ + public static final int PORT_OUTPUT = 1; + + private final int mId; + private final int mType; + private final int mAddress; + private final boolean mCecSupported; + private final boolean mArcSupported; + private final boolean mMhlSupported; + + /** + * Constructor. + * + * @param id identifier assigned to each port. 1 for HDMI port 1 + * @param type HDMI port input/output type + * @param address physical address of the port + * @param cec {@code true} if HDMI-CEC is supported on the port + * @param mhl {@code true} if MHL is supported on the port + * @param arc {@code true} if audio return channel is supported on the port + */ + public HdmiPortInfo(int id, int type, int address, boolean cec, boolean mhl, boolean arc) { + mId = id; + mType = type; + mAddress = address; + mCecSupported = cec; + mArcSupported = arc; + mMhlSupported = mhl; + } + + /** + * Returns the port id. + * + * @return port id + */ + public int getId() { + return mId; + } + + /** + * Returns the port type. + * + * @return port type + */ + public int getType() { + return mType; + } + + /** + * Returns the port address. + * + * @return port address + */ + public int getAddress() { + return mAddress; + } + + /** + * Returns {@code true} if the port supports HDMI-CEC signaling. + * + * @return {@code true} if the port supports HDMI-CEC signaling. + */ + public boolean isCecSupported() { + return mCecSupported; + } + + /** + * Returns {@code true} if the port supports MHL signaling. + * + * @return {@code true} if the port supports MHL signaling. + */ + public boolean isMhlSupported() { + return mMhlSupported; + } + + /** + * Returns {@code true} if the port supports audio return channel. + * + * @return {@code true} if the port supports audio return channel + */ + public boolean isArcSupported() { + return mArcSupported; + } + + /** + * Describe the kinds of special objects contained in this Parcelable's + * marshalled representation. + */ + @Override + public int describeContents() { + return 0; + } + + + /** + * A helper class to deserialize {@link HdmiPortInfo} for a parcel. + */ + public static final Parcelable.Creator<HdmiPortInfo> CREATOR = + new Parcelable.Creator<HdmiPortInfo>() { + @Override + public HdmiPortInfo createFromParcel(Parcel source) { + int id = source.readInt(); + int type = source.readInt(); + int address = source.readInt(); + boolean cec = (source.readInt() == 1); + boolean arc = (source.readInt() == 1); + boolean mhl = (source.readInt() == 1); + return new HdmiPortInfo(id, type, address, cec, arc, mhl); + } + + @Override + public HdmiPortInfo[] newArray(int size) { + return new HdmiPortInfo[size]; + } + }; + + /** + * Serialize this object into a {@link Parcel}. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mId); + dest.writeInt(mType); + dest.writeInt(mAddress); + dest.writeInt(mCecSupported ? 1 : 0); + dest.writeInt(mArcSupported ? 1 : 0); + dest.writeInt(mMhlSupported ? 1 : 0); + } +} diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java index 6dc4a4f..85af3d1 100644 --- a/core/java/android/hardware/hdmi/HdmiTvClient.java +++ b/core/java/android/hardware/hdmi/HdmiTvClient.java @@ -16,6 +16,8 @@ package android.hardware.hdmi; import android.annotation.SystemApi; +import android.os.RemoteException; +import android.util.Log; /** * HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system @@ -33,4 +35,46 @@ public final class HdmiTvClient { HdmiTvClient(IHdmiControlService service) { mService = service; } + + // Factory method for HdmiTvClient. + // Declared package-private. Accessed by HdmiControlManager only. + static HdmiTvClient create(IHdmiControlService service) { + return new HdmiTvClient(service); + } + + /** + * Callback interface used to get the result of {@link #deviceSelect}. + */ + public interface SelectCallback { + /** + * Called when the operation is finished. + * + * @param result the result value of {@link #deviceSelect} + */ + void onComplete(int result); + } + + /** + * Select a CEC logical device to be a new active source. + * + * @param logicalAddress + * @param callback + */ + public void deviceSelect(int logicalAddress, SelectCallback callback) { + // TODO: Replace SelectCallback with PartialResult. + try { + mService.deviceSelect(logicalAddress, getCallbackWrapper(callback)); + } catch (RemoteException e) { + Log.e(TAG, "failed to select device: ", e); + } + } + + private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) { + return new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) { + callback.onComplete(result); + } + }; + } } diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 8da38e1..8d7c638 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -33,4 +33,5 @@ interface IHdmiControlService { void queryDisplayStatus(IHdmiControlCallback callback); void addHotplugEventListener(IHdmiHotplugEventListener listener); void removeHotplugEventListener(IHdmiHotplugEventListener listener); + void deviceSelect(int logicalAddress, IHdmiControlCallback callback); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 27402668..ba31243 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -429,6 +429,11 @@ public class ConnectivityManager { */ public final static int INVALID_NET_ID = 0; + /** + * @hide + */ + public final static int REQUEST_ID_UNSET = 0; + private final IConnectivityManager mService; private final String mPackageName; @@ -863,10 +868,10 @@ public class ConnectivityManager { return -1; } - NetworkRequest request = removeRequestForFeature(netCap); - if (request != null) { + NetworkCallback networkCallback = removeRequestForFeature(netCap); + if (networkCallback != null) { Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature); - releaseNetworkRequest(request); + unregisterNetworkCallback(networkCallback); } return 1; } @@ -883,8 +888,8 @@ public class ConnectivityManager { * @hide */ public static void maybeMarkCapabilitiesRestricted(NetworkCapabilities nc) { - for (Integer capability : nc.getNetworkCapabilities()) { - switch (capability.intValue()) { + for (int capability : nc.getCapabilities()) { + switch (capability) { case NetworkCapabilities.NET_CAPABILITY_CBS: case NetworkCapabilities.NET_CAPABILITY_DUN: case NetworkCapabilities.NET_CAPABILITY_EIMS: @@ -903,7 +908,7 @@ public class ConnectivityManager { } // All the capabilities are typically provided by restricted networks. // Conclude that this network is restricted. - nc.removeNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); } private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) { @@ -927,15 +932,14 @@ public class ConnectivityManager { return null; } NetworkCapabilities netCap = new NetworkCapabilities(); - netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - netCap.addNetworkCapability(cap); + netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addCapability(cap); maybeMarkCapabilitiesRestricted(netCap); return netCap; } else if (networkType == TYPE_WIFI) { if ("p2p".equals(feature)) { NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); - netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P); + netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P); maybeMarkCapabilitiesRestricted(netCap); return netCap; } @@ -978,15 +982,15 @@ public class ConnectivityManager { int expireSequenceNumber; Network currentNetwork; int delay = -1; - NetworkCallbackListener networkCallbackListener = new NetworkCallbackListener() { + NetworkCallback networkCallback = new NetworkCallback() { @Override - public void onAvailable(NetworkRequest request, Network network) { + public void onAvailable(Network network) { currentNetwork = network; Log.d(TAG, "startUsingNetworkFeature got Network:" + network); setProcessDefaultNetworkForHostResolution(network); } @Override - public void onLost(NetworkRequest request, Network network) { + public void onLost(Network network) { if (network.equals(currentNetwork)) { currentNetwork = null; setProcessDefaultNetworkForHostResolution(null); @@ -1020,7 +1024,7 @@ public class ConnectivityManager { if (l == null) return; ourSeqNum = l.expireSequenceNumber; if (l.expireSequenceNumber == sequenceNum) { - releaseNetworkRequest(l.networkRequest); + unregisterNetworkCallback(l.networkCallback); sLegacyRequests.remove(netCap); } } @@ -1037,7 +1041,7 @@ public class ConnectivityManager { l.networkCapabilities = netCap; l.delay = delay; l.expireSequenceNumber = 0; - l.networkRequest = sendRequestForNetwork(netCap, l.networkCallbackListener, 0, + l.networkRequest = sendRequestForNetwork(netCap, l.networkCallback, 0, REQUEST, type); if (l.networkRequest == null) return null; sLegacyRequests.put(netCap, l); @@ -1053,11 +1057,11 @@ public class ConnectivityManager { } } - private NetworkRequest removeRequestForFeature(NetworkCapabilities netCap) { + private NetworkCallback removeRequestForFeature(NetworkCapabilities netCap) { synchronized (sLegacyRequests) { LegacyRequest l = sLegacyRequests.remove(netCap); if (l == null) return null; - return l.networkRequest; + return l.networkCallback; } } @@ -1180,8 +1184,8 @@ public class ConnectivityManager { } /** - * Callback for use with {@link ConnectivityManager#registerNetworkActiveListener} to - * find out when the current network has gone in to a high power state. + * Callback for use with {@link ConnectivityManager#registerDefaultNetworkActiveListener} + * to find out when the system default network has gone in to a high power state. */ public interface OnNetworkActiveListener { /** @@ -1190,7 +1194,7 @@ public class ConnectivityManager { * operations. Note that this listener only tells you when the network becomes * active; if at any other time you want to know whether it is active (and thus okay * to initiate network traffic), you can retrieve its instantaneous state with - * {@link ConnectivityManager#isNetworkActive}. + * {@link ConnectivityManager#isDefaultNetworkActive}. */ public void onNetworkActive(); } @@ -1211,13 +1215,18 @@ public class ConnectivityManager { = new ArrayMap<OnNetworkActiveListener, INetworkActivityListener>(); /** - * Start listening to reports when the data network is active, meaning it is - * a good time to perform network traffic. Use {@link #isNetworkActive()} - * to determine the current state of the network after registering the listener. + * Start listening to reports when the system's default data network is active, meaning it is + * a good time to perform network traffic. Use {@link #isDefaultNetworkActive()} + * to determine the current state of the system's default network after registering the + * listener. + * <p> + * If the process default network has been set with + * {@link ConnectivityManager#setProcessDefaultNetwork} this function will not + * reflect the process's default, but the system default. * * @param l The listener to be told when the network is active. */ - public void registerNetworkActiveListener(final OnNetworkActiveListener l) { + public void registerDefaultNetworkActiveListener(final OnNetworkActiveListener l) { INetworkActivityListener rl = new INetworkActivityListener.Stub() { @Override public void onNetworkActive() throws RemoteException { @@ -1234,11 +1243,11 @@ public class ConnectivityManager { /** * Remove network active listener previously registered with - * {@link #registerNetworkActiveListener}. + * {@link #registerDefaultNetworkActiveListener}. * * @param l Previously registered listener. */ - public void unregisterNetworkActiveListener(OnNetworkActiveListener l) { + public void unregisterDefaultNetworkActiveListener(OnNetworkActiveListener l) { INetworkActivityListener rl = mNetworkActivityListeners.get(l); if (rl == null) { throw new IllegalArgumentException("Listener not registered: " + l); @@ -1257,7 +1266,7 @@ public class ConnectivityManager { * this state. This method tells you whether right now is currently a good time to * initiate network traffic, as the network is already active. */ - public boolean isNetworkActive() { + public boolean isDefaultNetworkActive() { try { return getNetworkManagementService().isNetworkActive(); } catch (RemoteException e) { @@ -1889,7 +1898,7 @@ public class ConnectivityManager { * Base class for NetworkRequest callbacks. Used for notifications about network * changes. Should be extended by applications wanting notifications. */ - public static class NetworkCallbackListener { + public static class NetworkCallback { /** @hide */ public static final int PRECHECK = 1; /** @hide */ @@ -1912,78 +1921,68 @@ public class ConnectivityManager { * Called whenever the framework connects to a network that it may use to * satisfy this request */ - public void onPreCheck(NetworkRequest networkRequest, Network network) {} + public void onPreCheck(Network network) {} /** * Called when the framework connects and has declared new network ready for use. + * This callback may be called more than once if the {@link Network} that is + * satisfying the request changes. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. * @param network The {@link Network} of the satisfying network. */ - public void onAvailable(NetworkRequest networkRequest, Network network) {} + public void onAvailable(Network network) {} /** * Called when the network is about to be disconnected. Often paired with an - * {@link NetworkCallbackListener#onAvailable} call with the new replacement network + * {@link NetworkCallback#onAvailable} call with the new replacement network * for graceful handover. This may not be called if we have a hard loss * (loss without warning). This may be followed by either a - * {@link NetworkCallbackListener#onLost} call or a - * {@link NetworkCallbackListener#onAvailable} call for this network depending + * {@link NetworkCallback#onLost} call or a + * {@link NetworkCallback#onAvailable} call for this network depending * on whether we lose or regain it. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. - * @param network The {@link Network} of the failing network. - * @param maxSecToLive The time in seconds the framework will attempt to keep the - * network connected. Note that the network may suffers a + * @param network The {@link Network} that is about to be disconnected. + * @param maxMsToLive The time in ms the framework will attempt to keep the + * network connected. Note that the network may suffer a * hard loss at any time. */ - public void onLosing(NetworkRequest networkRequest, Network network, int maxSecToLive) {} + public void onLosing(Network network, int maxMsToLive) {} /** * Called when the framework has a hard loss of the network or when the * graceful failure ends. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. * @param network The {@link Network} lost. */ - public void onLost(NetworkRequest networkRequest, Network network) {} + public void onLost(Network network) {} /** * Called if no network is found in the given timeout time. If no timeout is given, * this will not be called. * @hide */ - public void onUnavailable(NetworkRequest networkRequest) {} + public void onUnavailable() {} /** * Called when the network the framework connected to for this request * changes capabilities but still satisfies the stated need. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. * @param network The {@link Network} whose capabilities have changed. * @param networkCapabilities The new {@link NetworkCapabilities} for this network. */ - public void onNetworkCapabilitiesChanged(NetworkRequest networkRequest, Network network, + public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {} /** * Called when the network the framework connected to for this request * changes {@link LinkProperties}. * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. * @param network The {@link Network} whose link properties have changed. * @param linkProperties The new {@link LinkProperties} for this network. */ - public void onLinkPropertiesChanged(NetworkRequest networkRequest, Network network, - LinkProperties linkProperties) {} + public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {} - /** - * Called when a {@link #releaseNetworkRequest} call concludes and the registered - * callbacks will no longer be used. - * - * @param networkRequest The {@link NetworkRequest} used to initiate the request. - */ - public void onReleased(NetworkRequest networkRequest) {} + private NetworkRequest networkRequest; } private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER; @@ -2009,12 +2008,12 @@ public class ConnectivityManager { private static final int EXPIRE_LEGACY_REQUEST = BASE + 10; private class CallbackHandler extends Handler { - private final HashMap<NetworkRequest, NetworkCallbackListener>mCallbackMap; + private final HashMap<NetworkRequest, NetworkCallback>mCallbackMap; private final AtomicInteger mRefCount; private static final String TAG = "ConnectivityManager.CallbackHandler"; private final ConnectivityManager mCm; - CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallbackListener>callbackMap, + CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallback>callbackMap, AtomicInteger refCount, ConnectivityManager cm) { super(looper); mCallbackMap = callbackMap; @@ -2028,9 +2027,9 @@ public class ConnectivityManager { switch (message.what) { case CALLBACK_PRECHECK: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { - callbacks.onPreCheck(request, getNetwork(message)); + callbacks.onPreCheck(getNetwork(message)); } else { Log.e(TAG, "callback not found for PRECHECK message"); } @@ -2038,9 +2037,9 @@ public class ConnectivityManager { } case CALLBACK_AVAILABLE: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { - callbacks.onAvailable(request, getNetwork(message)); + callbacks.onAvailable(getNetwork(message)); } else { Log.e(TAG, "callback not found for AVAILABLE message"); } @@ -2048,9 +2047,9 @@ public class ConnectivityManager { } case CALLBACK_LOSING: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { - callbacks.onLosing(request, getNetwork(message), message.arg1); + callbacks.onLosing(getNetwork(message), message.arg1); } else { Log.e(TAG, "callback not found for LOSING message"); } @@ -2058,9 +2057,9 @@ public class ConnectivityManager { } case CALLBACK_LOST: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { - callbacks.onLost(request, getNetwork(message)); + callbacks.onLost(getNetwork(message)); } else { Log.e(TAG, "callback not found for LOST message"); } @@ -2068,12 +2067,12 @@ public class ConnectivityManager { } case CALLBACK_UNAVAIL: { NetworkRequest req = (NetworkRequest)message.obj; - NetworkCallbackListener callbacks = null; + NetworkCallback callbacks = null; synchronized(mCallbackMap) { callbacks = mCallbackMap.get(req); } if (callbacks != null) { - callbacks.onUnavailable(req); + callbacks.onUnavailable(); } else { Log.e(TAG, "callback not found for UNAVAIL message"); } @@ -2081,12 +2080,12 @@ public class ConnectivityManager { } case CALLBACK_CAP_CHANGED: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { Network network = getNetwork(message); NetworkCapabilities cap = mCm.getNetworkCapabilities(network); - callbacks.onNetworkCapabilitiesChanged(request, network, cap); + callbacks.onCapabilitiesChanged(network, cap); } else { Log.e(TAG, "callback not found for CHANGED message"); } @@ -2094,12 +2093,12 @@ public class ConnectivityManager { } case CALLBACK_IP_CHANGED: { NetworkRequest request = getNetworkRequest(message); - NetworkCallbackListener callbacks = getCallbacks(request); + NetworkCallback callbacks = getCallbacks(request); if (callbacks != null) { Network network = getNetwork(message); LinkProperties lp = mCm.getLinkProperties(network); - callbacks.onLinkPropertiesChanged(request, network, lp); + callbacks.onLinkPropertiesChanged(network, lp); } else { Log.e(TAG, "callback not found for CHANGED message"); } @@ -2107,20 +2106,19 @@ public class ConnectivityManager { } case CALLBACK_RELEASED: { NetworkRequest req = (NetworkRequest)message.obj; - NetworkCallbackListener callbacks = null; + NetworkCallback callbacks = null; synchronized(mCallbackMap) { callbacks = mCallbackMap.remove(req); } if (callbacks != null) { - callbacks.onReleased(req); + synchronized(mRefCount) { + if (mRefCount.decrementAndGet() == 0) { + getLooper().quit(); + } + } } else { Log.e(TAG, "callback not found for CANCELED message"); } - synchronized(mRefCount) { - if (mRefCount.decrementAndGet() == 0) { - getLooper().quit(); - } - } break; } case CALLBACK_EXIT: { @@ -2138,7 +2136,7 @@ public class ConnectivityManager { private NetworkRequest getNetworkRequest(Message msg) { return (NetworkRequest)(msg.obj); } - private NetworkCallbackListener getCallbacks(NetworkRequest req) { + private NetworkCallback getCallbacks(NetworkRequest req) { synchronized(mCallbackMap) { return mCallbackMap.get(req); } @@ -2146,7 +2144,7 @@ public class ConnectivityManager { private Network getNetwork(Message msg) { return new Network(msg.arg2); } - private NetworkCallbackListener removeCallbacks(Message msg) { + private NetworkCallback removeCallbacks(Message msg) { NetworkRequest req = (NetworkRequest)msg.obj; synchronized(mCallbackMap) { return mCallbackMap.remove(req); @@ -2154,19 +2152,19 @@ public class ConnectivityManager { } } - private void addCallbackListener() { + private void incCallbackHandlerRefCount() { synchronized(sCallbackRefCount) { if (sCallbackRefCount.incrementAndGet() == 1) { // TODO - switch this over to a ManagerThread or expire it when done HandlerThread callbackThread = new HandlerThread("ConnectivityManager"); callbackThread.start(); sCallbackHandler = new CallbackHandler(callbackThread.getLooper(), - sNetworkCallbackListener, sCallbackRefCount, this); + sNetworkCallback, sCallbackRefCount, this); } } } - private void removeCallbackListener() { + private void decCallbackHandlerRefCount() { synchronized(sCallbackRefCount) { if (sCallbackRefCount.decrementAndGet() == 0) { sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget(); @@ -2175,8 +2173,8 @@ public class ConnectivityManager { } } - static final HashMap<NetworkRequest, NetworkCallbackListener> sNetworkCallbackListener = - new HashMap<NetworkRequest, NetworkCallbackListener>(); + static final HashMap<NetworkRequest, NetworkCallback> sNetworkCallback = + new HashMap<NetworkRequest, NetworkCallback>(); static final AtomicInteger sCallbackRefCount = new AtomicInteger(0); static CallbackHandler sCallbackHandler = null; @@ -2184,51 +2182,48 @@ public class ConnectivityManager { private final static int REQUEST = 2; private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener, int timeoutSec, int action, + NetworkCallback networkCallback, int timeoutSec, int action, int legacyType) { - NetworkRequest networkRequest = null; - if (networkCallbackListener == null) { - throw new IllegalArgumentException("null NetworkCallbackListener"); + if (networkCallback == null) { + throw new IllegalArgumentException("null NetworkCallback"); } if (need == null) throw new IllegalArgumentException("null NetworkCapabilities"); try { - addCallbackListener(); + incCallbackHandlerRefCount(); if (action == LISTEN) { - networkRequest = mService.listenForNetwork(need, new Messenger(sCallbackHandler), - new Binder()); + networkCallback.networkRequest = mService.listenForNetwork(need, + new Messenger(sCallbackHandler), new Binder()); } else { - networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler), - timeoutSec, new Binder(), legacyType); + networkCallback.networkRequest = mService.requestNetwork(need, + new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType); } - if (networkRequest != null) { - synchronized(sNetworkCallbackListener) { - sNetworkCallbackListener.put(networkRequest, networkCallbackListener); + if (networkCallback.networkRequest != null) { + synchronized(sNetworkCallback) { + sNetworkCallback.put(networkCallback.networkRequest, networkCallback); } } } catch (RemoteException e) {} - if (networkRequest == null) removeCallbackListener(); - return networkRequest; + if (networkCallback.networkRequest == null) decCallbackHandlerRefCount(); + return networkCallback.networkRequest; } /** * Request a network to satisfy a set of {@link NetworkCapabilities}. * * This {@link NetworkRequest} will live until released via - * {@link #releaseNetworkRequest} or the calling application exits. + * {@link #unregisterNetworkCallback} or the calling application exits. * Status of the request can be followed by listening to the various - * callbacks described in {@link NetworkCallbackListener}. The {@link Network} + * callbacks described in {@link NetworkCallback}. The {@link Network} * can be used to direct traffic to the network. * - * @param need {@link NetworkCapabilities} required by this request. - * @param networkCallbackListener The {@link NetworkCallbackListener} to be utilized for this - * request. Note the callbacks can be shared by multiple - * requests and the NetworkRequest token utilized to - * determine to which request the callback relates. - * @return A {@link NetworkRequest} object identifying the request. + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this + * request. Note the callback must not be shared - they + * uniquely specify this request. */ - public NetworkRequest requestNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener) { - return sendRequestForNetwork(need, networkCallbackListener, 0, REQUEST, TYPE_NONE); + public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) { + sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, + REQUEST, TYPE_NONE); } /** @@ -2236,53 +2231,53 @@ public class ConnectivityManager { * by a timeout. * * This function behaves identically to the non-timedout version, but if a suitable - * network is not found within the given time (in Seconds) the - * {@link NetworkCallbackListener#unavailable} callback is called. The request must + * network is not found within the given time (in milliseconds) the + * {@link NetworkCallback#unavailable} callback is called. The request must * still be released normally by calling {@link releaseNetworkRequest}. - * @param need {@link NetworkCapabilities} required by this request. - * @param networkCallbackListener The callbacks to be utilized for this request. Note - * the callbacks can be shared by multiple requests and - * the NetworkRequest token utilized to determine to which - * request the callback relates. - * @param timeoutSec The time in seconds to attempt looking for a suitable network - * before {@link NetworkCallbackListener#unavailable} is called. - * @return A {@link NetworkRequest} object identifying the request. + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The callbacks to be utilized for this request. Note + * the callbacks must not be shared - they uniquely specify + * this request. + * @param timeoutMs The time in milliseconds to attempt looking for a suitable network + * before {@link NetworkCallback#unavailable} is called. * @hide */ - public NetworkRequest requestNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener, int timeoutSec) { - return sendRequestForNetwork(need, networkCallbackListener, timeoutSec, REQUEST, - TYPE_NONE); + public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, + int timeoutMs) { + sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, + REQUEST, TYPE_NONE); } /** - * The maximum number of seconds the framework will look for a suitable network + * The maximum number of milliseconds the framework will look for a suitable network * during a timeout-equiped call to {@link requestNetwork}. * {@hide} */ - public final static int MAX_NETWORK_REQUEST_TIMEOUT_SEC = 100 * 60; + public final static int MAX_NETWORK_REQUEST_TIMEOUT_MS = 100 * 60 * 1000; /** * The lookup key for a {@link Network} object included with the intent after * succesfully finding a network for the applications request. Retrieve it with * {@link android.content.Intent#getParcelableExtra(String)}. + * @hide */ public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork"; /** - * The lookup key for a {@link NetworkCapabilities} object included with the intent after + * The lookup key for a {@link NetworkRequest} object included with the intent after * succesfully finding a network for the applications request. Retrieve it with * {@link android.content.Intent#getParcelableExtra(String)}. + * @hide */ - public static final String EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES = - "networkRequestNetworkCapabilities"; + public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST = + "networkRequestNetworkRequest"; /** * Request a network to satisfy a set of {@link NetworkCapabilities}. * - * This function behavies identically to the callback-equiped version, but instead - * of {@link NetworkCallbackListener} a {@link PendingIntent} is used. This means + * This function behavies identically to the version that takes a NetworkCallback, but instead + * of {@link NetworkCallback} a {@link PendingIntent} is used. This means * the request may outlive the calling application and get called back when a suitable * network is found. * <p> @@ -2291,10 +2286,10 @@ public class ConnectivityManager { * <receiver> tag in an AndroidManifest.xml file * <p> * The operation Intent is delivered with two extras, a {@link Network} typed - * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkCapabilities} - * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES} containing + * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkRequest} + * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_REQUEST} containing * the original requests parameters. It is important to create a new, - * {@link NetworkCallbackListener} based request before completing the processing of the + * {@link NetworkCallback} based request before completing the processing of the * Intent to reserve the network or it will be released shortly after the Intent * is processed. * <p> @@ -2302,51 +2297,49 @@ public class ConnectivityManager { * two Intents defined by {@link Intent#filterEquals}), then it will be removed and * replaced by this one, effectively releasing the previous {@link NetworkRequest}. * <p> - * The request may be released normally by calling {@link #releaseNetworkRequest}. + * The request may be released normally by calling {@link #unregisterNetworkCallback}. * - * @param need {@link NetworkCapabilities} required by this request. + * @param request {@link NetworkRequest} describing this request. * @param operation Action to perform when the network is available (corresponds - * to the {@link NetworkCallbackListener#onAvailable} call. Typically + * to the {@link NetworkCallback#onAvailable} call. Typically * comes from {@link PendingIntent#getBroadcast}. - * @return A {@link NetworkRequest} object identifying the request. + * @hide */ - public NetworkRequest requestNetwork(NetworkCapabilities need, PendingIntent operation) { + public void requestNetwork(NetworkRequest request, PendingIntent operation) { try { - return mService.pendingRequestForNetwork(need, operation); + mService.pendingRequestForNetwork(request.networkCapabilities, operation); } catch (RemoteException e) {} - return null; } /** * Registers to receive notifications about all networks which satisfy the given - * {@link NetworkCapabilities}. The callbacks will continue to be called until - * either the application exits or the request is released using - * {@link #releaseNetworkRequest}. + * {@link NetworkRequest}. The callbacks will continue to be called until + * either the application exits or {@link #unregisterNetworkCallback} is called * - * @param need {@link NetworkCapabilities} required by this request. - * @param networkCallbackListener The {@link NetworkCallbackListener} to be called as suitable - * networks change state. - * @return A {@link NetworkRequest} object identifying the request. + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} that the system will call as suitable + * networks change state. */ - public NetworkRequest listenForNetwork(NetworkCapabilities need, - NetworkCallbackListener networkCallbackListener) { - return sendRequestForNetwork(need, networkCallbackListener, 0, LISTEN, TYPE_NONE); + public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) { + sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, LISTEN, TYPE_NONE); } /** - * Releases a {@link NetworkRequest} generated either through a {@link #requestNetwork} - * or a {@link #listenForNetwork} call. The {@link NetworkCallbackListener} given in the - * earlier call may continue receiving calls until the - * {@link NetworkCallbackListener#onReleased} function is called, signifying the end - * of the request. + * Unregisters callbacks about and possibly releases networks originating from + * {@link #requestNetwork} and {@link #registerNetworkCallback} calls. If the + * given {@code NetworkCallback} had previosuly been used with {@code #requestNetwork}, + * any networks that had been connected to only to satisfy that request will be + * disconnected. * - * @param networkRequest The {@link NetworkRequest} generated by an earlier call to - * {@link #requestNetwork} or {@link #listenForNetwork}. + * @param networkCallback The {@link NetworkCallback} used when making the request. */ - public void releaseNetworkRequest(NetworkRequest networkRequest) { - if (networkRequest == null) throw new IllegalArgumentException("null NetworkRequest"); + public void unregisterNetworkCallback(NetworkCallback networkCallback) { + if (networkCallback == null || networkCallback.networkRequest == null || + networkCallback.networkRequest.requestId == REQUEST_ID_UNSET) { + throw new IllegalArgumentException("Invalid NetworkCallback"); + } try { - mService.releaseNetworkRequest(networkRequest); + mService.releaseNetworkRequest(networkCallback.networkRequest); } catch (RemoteException e) {} } diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java index dfe0384..a14d13f 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -42,7 +42,7 @@ import java.util.Arrays; * * Objects of this class are immutable. */ -public class IpPrefix implements Parcelable { +public final class IpPrefix implements Parcelable { private final byte[] address; // network byte order private final int prefixLength; @@ -139,7 +139,6 @@ public class IpPrefix implements Parcelable { /** * Implement the Parcelable interface. - * @hide */ public int describeContents() { return 0; @@ -147,7 +146,6 @@ public class IpPrefix implements Parcelable { /** * Implement the Parcelable interface. - * @hide */ public void writeToParcel(Parcel dest, int flags) { dest.writeByteArray(address); @@ -156,7 +154,6 @@ public class IpPrefix implements Parcelable { /** * Implement the Parcelable interface. - * @hide */ public static final Creator<IpPrefix> CREATOR = new Creator<IpPrefix>() { diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index bb05936..8eefa0f 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -44,7 +44,7 @@ import java.util.List; * does not affect live networks. * */ -public class LinkProperties implements Parcelable { +public final class LinkProperties implements Parcelable { // The interface described by the network link. private String mIfaceName; private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>(); @@ -463,7 +463,6 @@ public class LinkProperties implements Parcelable { /** * Implement the Parcelable interface - * @hide */ public int describeContents() { return 0; diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index d933f26..318aabe 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -29,8 +29,9 @@ import javax.net.SocketFactory; /** * Identifies a {@code Network}. This is supplied to applications via - * {@link ConnectivityManager.NetworkCallbackListener} in response to - * {@link ConnectivityManager#requestNetwork} or {@link ConnectivityManager#listenForNetwork}. + * {@link ConnectivityManager.NetworkCallback} in response to the active + * {@link ConnectivityManager#requestNetwork} or passive + * {@link ConnectivityManager#registerNetworkCallback} calls. * It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis * through a targeted {@link SocketFactory} or process-wide via * {@link ConnectivityManager#setProcessDefaultNetwork}. diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 35274f1..fe96287 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -44,6 +44,9 @@ public final class NetworkCapabilities implements Parcelable { private static final String TAG = "NetworkCapabilities"; private static final boolean DBG = false; + /** + * @hide + */ public NetworkCapabilities() { } @@ -154,58 +157,64 @@ public final class NetworkCapabilities implements Parcelable { * Multiple capabilities may be applied sequentially. Note that when searching * for a network to satisfy a request, all capabilities requested must be satisfied. * - * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added. + * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added. + * @return This NetworkCapability to facilitate chaining. + * @hide */ - public void addNetworkCapability(int networkCapability) { - if (networkCapability < MIN_NET_CAPABILITY || - networkCapability > MAX_NET_CAPABILITY) { + public NetworkCapabilities addCapability(int capability) { + if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { throw new IllegalArgumentException("NetworkCapability out of range"); } - mNetworkCapabilities |= 1 << networkCapability; + mNetworkCapabilities |= 1 << capability; + return this; } /** * Removes (if found) the given capability from this {@code NetworkCapability} instance. * - * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed. + * @param capability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed. + * @return This NetworkCapability to facilitate chaining. + * @hide */ - public void removeNetworkCapability(int networkCapability) { - if (networkCapability < MIN_NET_CAPABILITY || - networkCapability > MAX_NET_CAPABILITY) { + public NetworkCapabilities removeCapability(int capability) { + if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { throw new IllegalArgumentException("NetworkCapability out of range"); } - mNetworkCapabilities &= ~(1 << networkCapability); + mNetworkCapabilities &= ~(1 << capability); + return this; } /** * Gets all the capabilities set on this {@code NetworkCapability} instance. * - * @return a {@link Collection} of {@code NetworkCapabilities.NET_CAPABILITY_*} values + * @return an array of {@code NetworkCapabilities.NET_CAPABILITY_*} values * for this instance. + * @hide */ - public Collection<Integer> getNetworkCapabilities() { + public int[] getCapabilities() { return enumerateBits(mNetworkCapabilities); } /** * Tests for the presence of a capabilitity on this instance. * - * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for. + * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for. * @return {@code true} if set on this instance. */ - public boolean hasCapability(int networkCapability) { - if (networkCapability < MIN_NET_CAPABILITY || - networkCapability > MAX_NET_CAPABILITY) { + public boolean hasCapability(int capability) { + if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { return false; } - return ((mNetworkCapabilities & (1 << networkCapability)) != 0); + return ((mNetworkCapabilities & (1 << capability)) != 0); } - private Collection<Integer> enumerateBits(long val) { - ArrayList<Integer> result = new ArrayList<Integer>(); + private int[] enumerateBits(long val) { + int size = Long.bitCount(val); + int[] result = new int[size]; + int index = 0; int resource = 0; while (val > 0) { - if ((val & 1) == 1) result.add(resource); + if ((val & 1) == 1) result[index++] = resource; val = val >> 1; resource++; } @@ -265,33 +274,40 @@ public final class NetworkCapabilities implements Parcelable { * {@code NetworkCapabilities.NET_CAPABILITY_*} listed above. * * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be added. + * @return This NetworkCapability to facilitate chaining. + * @hide */ - public void addTransportType(int transportType) { + public NetworkCapabilities addTransportType(int transportType) { if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) { throw new IllegalArgumentException("TransportType out of range"); } mTransportTypes |= 1 << transportType; + return this; } /** * Removes (if found) the given transport from this {@code NetworkCapability} instance. * * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be removed. + * @return This NetworkCapability to facilitate chaining. + * @hide */ - public void removeTransportType(int transportType) { + public NetworkCapabilities removeTransportType(int transportType) { if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) { throw new IllegalArgumentException("TransportType out of range"); } mTransportTypes &= ~(1 << transportType); + return this; } /** * Gets all the transports set on this {@code NetworkCapability} instance. * - * @return a {@link Collection} of {@code NetworkCapabilities.TRANSPORT_*} values + * @return an array of {@code NetworkCapabilities.TRANSPORT_*} values * for this instance. + * @hide */ - public Collection<Integer> getTransportTypes() { + public int[] getTransportTypes() { return enumerateBits(mTransportTypes); } @@ -340,6 +356,7 @@ public final class NetworkCapabilities implements Parcelable { * fast backhauls and slow backhauls. * * @param upKbps the estimated first hop upstream (device to network) bandwidth. + * @hide */ public void setLinkUpstreamBandwidthKbps(int upKbps) { mLinkUpBandwidthKbps = upKbps; @@ -368,6 +385,7 @@ public final class NetworkCapabilities implements Parcelable { * fast backhauls and slow backhauls. * * @param downKbps the estimated first hop downstream (network to device) bandwidth. + * @hide */ public void setLinkDownstreamBandwidthKbps(int downKbps) { mLinkDownBandwidthKbps = downKbps; @@ -464,24 +482,22 @@ public final class NetworkCapabilities implements Parcelable { }; public String toString() { - Collection<Integer> types = getTransportTypes(); - String transports = (types.size() > 0 ? " Transports: " : ""); - Iterator<Integer> i = types.iterator(); - while (i.hasNext()) { - switch (i.next()) { + int[] types = getTransportTypes(); + String transports = (types.length > 0 ? " Transports: " : ""); + for (int i = 0; i < types.length;) { + switch (types[i]) { case TRANSPORT_CELLULAR: transports += "CELLULAR"; break; case TRANSPORT_WIFI: transports += "WIFI"; break; case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break; case TRANSPORT_ETHERNET: transports += "ETHERNET"; break; } - if (i.hasNext()) transports += "|"; + if (++i < types.length) transports += "|"; } - types = getNetworkCapabilities(); - String capabilities = (types.size() > 0 ? " Capabilities: " : ""); - i = types.iterator(); - while (i.hasNext()) { - switch (i.next().intValue()) { + types = getCapabilities(); + String capabilities = (types.length > 0 ? " Capabilities: " : ""); + for (int i = 0; i < types.length; ) { + switch (types[i]) { case NET_CAPABILITY_MMS: capabilities += "MMS"; break; case NET_CAPABILITY_SUPL: capabilities += "SUPL"; break; case NET_CAPABILITY_DUN: capabilities += "DUN"; break; @@ -497,7 +513,7 @@ public final class NetworkCapabilities implements Parcelable { case NET_CAPABILITY_INTERNET: capabilities += "INTERNET"; break; case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break; } - if (i.hasNext()) capabilities += "&"; + if (++i < types.length) capabilities += "&"; } String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" + diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 47377e9..36dc573 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -22,19 +22,14 @@ import android.os.Parcelable; import java.util.concurrent.atomic.AtomicInteger; /** - * Defines a request for a network, made by calling {@link ConnectivityManager#requestNetwork} - * or {@link ConnectivityManager#listenForNetwork}. - * - * This token records the {@link NetworkCapabilities} used to make the request and identifies - * the request. It should be used to release the request via - * {@link ConnectivityManager#releaseNetworkRequest} when the network is no longer desired. + * Defines a request for a network, made through {@link NetworkRequest.Builder} and used + * to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes + * via {@link ConnectivityManager#registerNetworkCallback}. */ public class NetworkRequest implements Parcelable { /** - * The {@link NetworkCapabilities} that define this request. This should not be modified. - * The networkCapabilities of the request are set when - * {@link ConnectivityManager#requestNetwork} is called and the value is presented here - * as a convenient reminder of what was requested. + * The {@link NetworkCapabilities} that define this request. + * @hide */ public final NetworkCapabilities networkCapabilities; @@ -71,6 +66,95 @@ public class NetworkRequest implements Parcelable { this.legacyType = that.legacyType; } + /** + * Builder used to create {@link NetworkRequest} objects. Specify the Network features + * needed in terms of {@link NetworkCapabilities} features + */ + public static class Builder { + private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities(); + + /** + * Default constructor for Builder. + */ + public Builder() {} + + /** + * Build {@link NetworkRequest} give the current set of capabilities. + */ + public NetworkRequest build() { + return new NetworkRequest(mNetworkCapabilities, ConnectivityManager.TYPE_NONE, + ConnectivityManager.REQUEST_ID_UNSET); + } + + /** + * Add the given capability requirement to this builder. These represent + * the requested network's required capabilities. Note that when searching + * for a network to satisfy a request, all capabilities requested must be + * satisfied. See {@link NetworkCapabilities} for {@code NET_CAPABILITIY_*} + * definitions. + * + * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to add. + * @return The builder to facilitate chaining + * {@code builder.addCapability(...).addCapability();}. + */ + public Builder addCapability(int capability) { + mNetworkCapabilities.addCapability(capability); + return this; + } + + /** + * Removes (if found) the given capability from this builder instance. + * + * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to remove. + * @return The builder to facilitate chaining. + */ + public Builder removeCapability(int capability) { + mNetworkCapabilities.removeCapability(capability); + return this; + } + + /** + * Adds the given transport requirement to this builder. These represent + * the set of allowed transports for the request. Only networks using one + * of these transports will satisfy the request. If no particular transports + * are required, none should be specified here. See {@link NetworkCapabilities} + * for {@code TRANSPORT_*} definitions. + * + * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to add. + * @return The builder to facilitate chaining. + */ + public Builder addTransportType(int transportType) { + mNetworkCapabilities.addTransportType(transportType); + return this; + } + + /** + * Removes (if found) the given transport from this builder instance. + * + * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to remove. + * @return The builder to facilitate chaining. + */ + public Builder removeTransportType(int transportType) { + mNetworkCapabilities.removeTransportType(transportType); + return this; + } + + /** + * @hide + */ + public Builder setLinkUpstreamBandwidthKbps(int upKbps) { + mNetworkCapabilities.setLinkUpstreamBandwidthKbps(upKbps); + return this; + } + /** + * @hide + */ + public Builder setLinkDownstreamBandwidthKbps(int downKbps) { + mNetworkCapabilities.setLinkDownstreamBandwidthKbps(downKbps); + return this; + } + } + // implement the Parcelable interface public int describeContents() { return 0; diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index af27e1d..63d6cd3 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -46,7 +46,7 @@ import java.util.Objects; * destination and gateway are both specified, they must be of the same address family * (IPv4 or IPv6). */ -public class RouteInfo implements Parcelable { +public final class RouteInfo implements Parcelable { /** * The IP destination address for this route. * TODO: Make this an IpPrefix. @@ -248,7 +248,7 @@ public class RouteInfo implements Parcelable { * Retrieves the gateway or next hop {@link InetAddress} for this route. * * @return {@link InetAddress} specifying the gateway or next hop. This may be - & {@code null} for a directly-connected route." + * {@code null} for a directly-connected route." */ public InetAddress getGateway() { return mGateway; @@ -361,7 +361,7 @@ public class RouteInfo implements Parcelable { RouteInfo target = (RouteInfo) obj; - return Objects.equals(mDestination, target.getDestination()) && + return Objects.equals(mDestination, target.getDestinationLinkAddress()) && Objects.equals(mGateway, target.getGateway()) && Objects.equals(mInterface, target.getInterface()); } @@ -378,7 +378,6 @@ public class RouteInfo implements Parcelable { /** * Implement the Parcelable interface - * @hide */ public int describeContents() { return 0; @@ -386,7 +385,6 @@ public class RouteInfo implements Parcelable { /** * Implement the Parcelable interface - * @hide */ public void writeToParcel(Parcel dest, int flags) { if (mDestination == null) { @@ -409,7 +407,6 @@ public class RouteInfo implements Parcelable { /** * Implement the Parcelable interface. - * @hide */ public static final Creator<RouteInfo> CREATOR = new Creator<RouteInfo>() { diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index cd47099..e77ef95 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -39,9 +39,6 @@ interface IUserManager { UserInfo getProfileParent(int userHandle); UserInfo getUserInfo(int userHandle); boolean isRestricted(); - void setGuestEnabled(boolean enable); - boolean isGuestEnabled(); - void wipeUser(int userHandle); int getUserSerialNumber(int userHandle); int getUserHandle(int userSerialNumber); Bundle getUserRestrictions(int userHandle); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 91fbb9d..eb3c748 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -25,6 +25,7 @@ import android.graphics.Bitmap.Config; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.provider.Settings; import android.util.Log; import com.android.internal.R; @@ -361,6 +362,16 @@ public class UserManager { } /** + * Checks if the calling app is running as a guest user. + * @return whether the caller is a guest user. + * @hide + */ + public boolean isGuestUser() { + UserInfo user = getUserInfo(UserHandle.myUserId()); + return user != null ? user.isGuest() : false; + } + + /** * Return whether the given user is actively running. This means that * the user is in the "started" state, not "stopped" -- it is currently * allowed to run code through scheduled alarms, receiving broadcasts, @@ -550,6 +561,21 @@ public class UserManager { } /** + * Creates a guest user and configures it. + * @param context an application context + * @param name the name to set for the user + * @hide + */ + public UserInfo createGuest(Context context, String name) { + UserInfo guest = createUser(name, UserInfo.FLAG_GUEST); + if (guest != null) { + Settings.Secure.putStringForUser(context.getContentResolver(), + Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id); + } + return guest; + } + + /** * Creates a user with the specified name and options as a profile of another user. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. * @@ -827,50 +853,6 @@ public class UserManager { } /** - * Enable or disable the use of a guest account. If disabled, the existing guest account - * will be wiped. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. - * @param enable whether to enable a guest account. - * @hide - */ - public void setGuestEnabled(boolean enable) { - try { - mService.setGuestEnabled(enable); - } catch (RemoteException re) { - Log.w(TAG, "Could not change guest account availability to " + enable); - } - } - - /** - * Checks if a guest user is enabled for this device. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. - * @return whether a guest user is enabled - * @hide - */ - public boolean isGuestEnabled() { - try { - return mService.isGuestEnabled(); - } catch (RemoteException re) { - Log.w(TAG, "Could not retrieve guest enabled state"); - return false; - } - } - - /** - * Wipes all the data for a user, but doesn't remove the user. - * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. - * @param userHandle - * @hide - */ - public void wipeUser(int userHandle) { - try { - mService.wipeUser(userHandle); - } catch (RemoteException re) { - Log.w(TAG, "Could not wipe user " + userHandle); - } - } - - /** * Returns the maximum number of users that can be created on this device. A return value * of 1 means that it is a single user device. * @hide diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index fa85903..3853003 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -156,8 +156,8 @@ public class Browser { * @param title Title for the bookmark. Can be null or empty string. * @param url Url for the bookmark. Can be null or empty string. */ - public static final void saveBookmark(Context c, - String title, + public static final void saveBookmark(Context c, + String title, String url) { Intent i = new Intent(Intent.ACTION_INSERT, Browser.BOOKMARKS_URI); i.putExtra("title", title); @@ -234,10 +234,10 @@ public class Browser { * * @param cr The ContentResolver used to access the database. */ - public static final Cursor getAllBookmarks(ContentResolver cr) throws + public static final Cursor getAllBookmarks(ContentResolver cr) throws IllegalStateException { return cr.query(Bookmarks.CONTENT_URI, - new String[] { Bookmarks.URL }, + new String[] { Bookmarks.URL }, Bookmarks.IS_FOLDER + " = 0", null, null); } @@ -398,24 +398,17 @@ public class Browser { // TODO make a single request to the provider to do this in a single transaction Cursor cursor = null; try { - + // Select non-bookmark history, ordered by date cursor = cr.query(History.CONTENT_URI, new String[] { History._ID, History.URL, History.DATE_LAST_VISITED }, null, null, History.DATE_LAST_VISITED + " ASC"); if (cursor.moveToFirst() && cursor.getCount() >= MAX_HISTORY_COUNT) { - WebIconDatabase iconDb = null; - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - iconDb = WebIconDatabase.getInstance(); - } /* eliminate oldest history items */ for (int i = 0; i < TRUNCATE_N_OLDEST; i++) { cr.delete(ContentUris.withAppendedId(History.CONTENT_URI, cursor.getLong(0)), null, null); - if (iconDb != null) { - iconDb.releaseIconForPageUrl(cursor.getString(1)); - } if (!cursor.moveToNext()) break; } } @@ -475,18 +468,6 @@ public class Browser { cursor = cr.query(History.CONTENT_URI, new String[] { History.URL }, whereClause, null, null); if (cursor.moveToFirst()) { - WebIconDatabase iconDb = null; - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - iconDb = WebIconDatabase.getInstance(); - } - do { - // Delete favicons - // TODO don't release if the URL is bookmarked - if (iconDb != null) { - iconDb.releaseIconForPageUrl(cursor.getString(0)); - } - } while (cursor.moveToNext()); - cr.delete(History.CONTENT_URI, whereClause, null); } } catch (IllegalStateException e) { @@ -531,7 +512,7 @@ public class Browser { * @param cr The ContentResolver used to access the database. * @param url url to remove. */ - public static final void deleteFromHistory(ContentResolver cr, + public static final void deleteFromHistory(ContentResolver cr, String url) { cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url }); } @@ -565,7 +546,7 @@ public class Browser { Log.e(LOGTAG, "clearSearches", e); } } - + /** * Request all icons from the database. This call must either be called * in the main thread or have had Looper.prepare() invoked in the calling @@ -574,14 +555,12 @@ public class Browser { * @param cr The ContentResolver used to access the database. * @param where Clause to be used to limit the query from the database. * Must be an allowable string to be passed into a database query. - * @param listener IconListener that gets the icons once they are + * @param listener IconListener that gets the icons once they are * retrieved. */ public static final void requestAllIcons(ContentResolver cr, String where, WebIconDatabase.IconListener listener) { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - WebIconDatabase.getInstance().bulkRequestIconForPageUrl(cr, where, listener); - } + // Do nothing: this is no longer used. } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index bec401e..3fe0fb8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -791,6 +791,15 @@ public final class Settings { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_PAIRING_SETTINGS = "android.settings.PAIRING_SETTINGS"; + /** + * Activity Action: Show battery saver settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_BATTERY_SAVER_SETTINGS + = "android.settings.BATTERY_SAVER_SETTINGS"; + // End of Intent actions for Settings /** @@ -4567,6 +4576,16 @@ public final class Settings { public static final String DISPLAY_INTERCEPTED_NOTIFICATIONS = "display_intercepted_notifications"; /** + * If enabled, apps should try to skip any introductory hints on first launch. This might + * apply to users that are already familiar with the environment or temporary users, like + * guests. + * <p> + * Type : int (0 to show hints, 1 to skip showing hints) + * @hide + */ + public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -6024,6 +6043,13 @@ public final class Settings { */ public static final String LOW_POWER_MODE = "low_power"; + /** + * Battery level [1-99] at which low power mode automatically turns on. + * If 0, it will not automatically turn on. + * @hide + */ + public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level"; + /** * If 1, the activity manager will aggressively finish activities and * processes as soon as they are no longer needed. If 0, the normal @@ -6200,6 +6226,14 @@ public final class Settings { public static final String DEVICE_NAME = "device_name"; /** + * Whether it should be possible to create a guest user on the device. + * <p> + * Type: int (0 for disabled, 1 for enabled) + * @hide + */ + public static final String GUEST_USER_ENABLED = "guest_user_enabled"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index fd475cd..8bd0f4d 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -29,6 +29,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.ArrayMap; import android.util.Log; import java.util.List; @@ -54,7 +55,7 @@ public abstract class NotificationListenerService extends Service { + "[" + getClass().getSimpleName() + "]"; private INotificationListenerWrapper mWrapper = null; - private Ranking mRanking; + private RankingMap mRankingMap; private INotificationManager mNoMan; @@ -75,7 +76,43 @@ public abstract class NotificationListenerService extends Service { * object as well as its identifying information (tag and id) and source * (package name). */ - public abstract void onNotificationPosted(StatusBarNotification sbn); + public void onNotificationPosted(StatusBarNotification sbn) { + // optional + } + + /** + * Implement this method to learn about new notifications as they are posted by apps. + * + * @param sbn A data structure encapsulating the original {@link android.app.Notification} + * object as well as its identifying information (tag and id) and source + * (package name). + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications, including the newly posted one. + */ + public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { + onNotificationPosted(sbn); + } + + /** + * Implement this method to learn when notifications are removed. + * <P> + * This might occur because the user has dismissed the notification using system UI (or another + * notification listener) or because the app has withdrawn the notification. + * <P> + * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the + * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight + * fields such as {@link android.app.Notification#contentView} and + * {@link android.app.Notification#largeIcon}. However, all other fields on + * {@link StatusBarNotification}, sufficient to match this call with a prior call to + * {@link #onNotificationPosted(StatusBarNotification)}, will be intact. + * + * @param sbn A data structure encapsulating at least the original information (tag and id) + * and source (package name) used to post the {@link android.app.Notification} that + * was just removed. + */ + public void onNotificationRemoved(StatusBarNotification sbn) { + // optional + } /** * Implement this method to learn when notifications are removed. @@ -93,8 +130,13 @@ public abstract class NotificationListenerService extends Service { * @param sbn A data structure encapsulating at least the original information (tag and id) * and source (package name) used to post the {@link android.app.Notification} that * was just removed. + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications. + * */ - public abstract void onNotificationRemoved(StatusBarNotification sbn); + public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) { + onNotificationRemoved(sbn); + } /** * Implement this method to learn about when the listener is enabled and connected to @@ -107,10 +149,11 @@ public abstract class NotificationListenerService extends Service { /** * Implement this method to be notified when the notification ranking changes. - * <P> - * Call {@link #getCurrentRanking()} to retrieve the new ranking. + * + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications. */ - public void onNotificationRankingUpdate() { + public void onNotificationRankingUpdate(RankingMap rankingMap) { // optional } @@ -241,16 +284,19 @@ public abstract class NotificationListenerService extends Service { * * <p> * The returned object represents the current ranking snapshot and only - * applies for currently active notifications. Hence you must retrieve a - * new Ranking after each notification event such as - * {@link #onNotificationPosted(StatusBarNotification)}, - * {@link #onNotificationRemoved(StatusBarNotification)}, etc. + * applies for currently active notifications. + * <p> + * Generally you should use the RankingMap that is passed with events such + * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)}, + * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and + * so on. This method should only be used when needing access outside of + * such events, for example to retrieve the RankingMap right after + * initialization. * - * @return A {@link NotificationListenerService.Ranking} object providing - * access to ranking information + * @return A {@link RankingMap} object providing access to ranking information */ - public Ranking getCurrentRanking() { - return mRanking; + public RankingMap getCurrentRanking() { + return mRankingMap; } @Override @@ -313,7 +359,7 @@ public abstract class NotificationListenerService extends Service { synchronized (mWrapper) { applyUpdate(update); try { - NotificationListenerService.this.onNotificationPosted(sbn); + NotificationListenerService.this.onNotificationPosted(sbn, mRankingMap); } catch (Throwable t) { Log.w(TAG, "Error running onNotificationPosted", t); } @@ -326,7 +372,7 @@ public abstract class NotificationListenerService extends Service { synchronized (mWrapper) { applyUpdate(update); try { - NotificationListenerService.this.onNotificationRemoved(sbn); + NotificationListenerService.this.onNotificationRemoved(sbn, mRankingMap); } catch (Throwable t) { Log.w(TAG, "Error running onNotificationRemoved", t); } @@ -351,7 +397,7 @@ public abstract class NotificationListenerService extends Service { synchronized (mWrapper) { applyUpdate(update); try { - NotificationListenerService.this.onNotificationRankingUpdate(); + NotificationListenerService.this.onNotificationRankingUpdate(mRankingMap); } catch (Throwable t) { Log.w(TAG, "Error running onNotificationRankingUpdate", t); } @@ -360,7 +406,65 @@ public abstract class NotificationListenerService extends Service { } private void applyUpdate(NotificationRankingUpdate update) { - mRanking = new Ranking(update); + mRankingMap = new RankingMap(update); + } + + /** + * Provides access to ranking information on a currently active + * notification. + * + * <p> + * Note that this object is not updated on notification events (such as + * {@link #onNotificationPosted(StatusBarNotification, RankingMap)}, + * {@link #onNotificationRemoved(StatusBarNotification)}, etc.). Make sure + * to retrieve a new Ranking from the current {@link RankingMap} whenever + * a notification event occurs. + */ + public static class Ranking { + private final String mKey; + private final int mRank; + private final boolean mIsAmbient; + private final boolean mIsInterceptedByDnd; + + private Ranking(String key, int rank, boolean isAmbient, boolean isInterceptedByDnd) { + mKey = key; + mRank = rank; + mIsAmbient = isAmbient; + mIsInterceptedByDnd = isInterceptedByDnd; + } + + /** + * Returns the key of the notification this Ranking applies to. + */ + public String getKey() { + return mKey; + } + + /** + * Returns the rank of the notification. + * + * @return the rank of the notification, that is the 0-based index in + * the list of active notifications. + */ + public int getRank() { + return mRank; + } + + /** + * Returns whether the notification is an ambient notification, that is + * a notification that doesn't require the user's immediate attention. + */ + public boolean isAmbient() { + return mIsAmbient; + } + + /** + * Returns whether the notification was intercepted by + * "Do not disturb". + */ + public boolean isInterceptedByDoNotDisturb() { + return mIsInterceptedByDnd; + } } /** @@ -371,11 +475,14 @@ public abstract class NotificationListenerService extends Service { * Note that this object represents a ranking snapshot that only applies to * notifications active at the time of retrieval. */ - public static class Ranking implements Parcelable { + public static class RankingMap implements Parcelable { private final NotificationRankingUpdate mRankingUpdate; + private final ArrayMap<String, Ranking> mRankingCache; + private boolean mRankingCacheInitialized; - private Ranking(NotificationRankingUpdate rankingUpdate) { + private RankingMap(NotificationRankingUpdate rankingUpdate) { mRankingUpdate = rankingUpdate; + mRankingCache = new ArrayMap<>(rankingUpdate.getOrderedKeys().length); } /** @@ -389,56 +496,37 @@ public abstract class NotificationListenerService extends Service { } /** - * Returns the rank of the notification with the given key, that is the - * index of <code>key</code> in the array of keys returned by - * {@link #getOrderedKeys()}. + * Returns the Ranking for the notification with the given key. * - * @return The rank of the notification with the given key; -1 when the - * given key is unknown. + * @return the Ranking of the notification with the given key; + * <code>null</code> when the key is unknown. */ - public int getRank(String key) { - // TODO: Optimize. - String[] orderedKeys = mRankingUpdate.getOrderedKeys(); - for (int i = 0; i < orderedKeys.length; i++) { - if (orderedKeys[i].equals(key)) { - return i; + public Ranking getRanking(String key) { + synchronized (mRankingCache) { + if (!mRankingCacheInitialized) { + initializeRankingCache(); + mRankingCacheInitialized = true; } } - return -1; + return mRankingCache.get(key); } - /** - * Returns whether the notification with the given key was intercepted - * by "Do not disturb". - */ - public boolean isInterceptedByDoNotDisturb(String key) { - // TODO: Optimize. - for (String interceptedKey : mRankingUpdate.getDndInterceptedKeys()) { - if (interceptedKey.equals(key)) { - return true; - } - } - return false; - } - - /** - * Returns whether the notification with the given key is an ambient - * notification, that is a notification that doesn't require the user's - * immediate attention. - */ - public boolean isAmbient(String key) { - // TODO: Optimize. - int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex(); - if (firstAmbientIndex < 0) { - return false; - } + private void initializeRankingCache() { String[] orderedKeys = mRankingUpdate.getOrderedKeys(); - for (int i = firstAmbientIndex; i < orderedKeys.length; i++) { - if (orderedKeys[i].equals(key)) { - return true; + int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex(); + for (int i = 0; i < orderedKeys.length; i++) { + String key = orderedKeys[i]; + boolean isAmbient = firstAmbientIndex > -1 && firstAmbientIndex <= i; + boolean isInterceptedByDnd = false; + // TODO: Optimize. + for (String s : mRankingUpdate.getDndInterceptedKeys()) { + if (s.equals(key)) { + isInterceptedByDnd = true; + break; + } } + mRankingCache.put(key, new Ranking(key, i, isAmbient, isInterceptedByDnd)); } - return false; } // ----------- Parcelable @@ -453,16 +541,16 @@ public abstract class NotificationListenerService extends Service { dest.writeParcelable(mRankingUpdate, flags); } - public static final Creator<Ranking> CREATOR = new Creator<Ranking>() { + public static final Creator<RankingMap> CREATOR = new Creator<RankingMap>() { @Override - public Ranking createFromParcel(Parcel source) { + public RankingMap createFromParcel(Parcel source) { NotificationRankingUpdate rankingUpdate = source.readParcelable(null); - return new Ranking(rankingUpdate); + return new RankingMap(rankingUpdate); } @Override - public Ranking[] newArray(int size) { - return new Ranking[size]; + public RankingMap[] newArray(int size) { + return new RankingMap[size]; } }; } diff --git a/core/java/android/service/voice/DspInfo.java b/core/java/android/service/voice/DspInfo.java new file mode 100644 index 0000000..0862309 --- /dev/null +++ b/core/java/android/service/voice/DspInfo.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.voice; + +import java.util.UUID; + +/** + * Properties of the DSP hardware on the device. + * @hide + */ +public class DspInfo { + /** + * Unique voice engine Id (changes with each version). + */ + public final UUID voiceEngineId; + + /** + * Human readable voice detection engine implementor. + */ + public final String voiceEngineImplementor; + /** + * Human readable voice detection engine description. + */ + public final String voiceEngineDescription; + /** + * Human readable voice detection engine version + */ + public final int voiceEngineVersion; + /** + * Rated power consumption when detection is active. + */ + public final int powerConsumptionMw; + + public DspInfo(UUID voiceEngineId, String voiceEngineImplementor, + String voiceEngineDescription, int version, int powerConsumptionMw) { + this.voiceEngineId = voiceEngineId; + this.voiceEngineImplementor = voiceEngineImplementor; + this.voiceEngineDescription = voiceEngineDescription; + this.voiceEngineVersion = version; + this.powerConsumptionMw = powerConsumptionMw; + } +} diff --git a/core/java/android/service/voice/KeyphraseEnrollmentInfo.java b/core/java/android/service/voice/KeyphraseEnrollmentInfo.java new file mode 100644 index 0000000..ebe41ce --- /dev/null +++ b/core/java/android/service/voice/KeyphraseEnrollmentInfo.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.voice; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.util.AttributeSet; +import android.util.Slog; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.List; + +/** @hide */ +public class KeyphraseEnrollmentInfo { + private static final String TAG = "KeyphraseEnrollmentInfo"; + /** + * Name under which a Hotword enrollment component publishes information about itself. + * This meta-data should reference an XML resource containing a + * <code><{@link + * android.R.styleable#VoiceEnrollmentApplication + * voice-enrollment-application}></code> tag. + */ + private static final String VOICE_KEYPHRASE_META_DATA = "android.voice_enrollment"; + /** + * Activity Action: Show activity for managing the keyphrases for hotword detection. + * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase + * detection. + */ + public static final String ACTION_MANAGE_VOICE_KEYPHRASES = + "com.android.intent.action.MANAGE_VOICE_KEYPHRASES"; + /** + * Intent extra: The intent extra for un-enrolling a user for a particular keyphrase. + */ + public static final String EXTRA_VOICE_KEYPHRASE_UNENROLL = + "com.android.intent.extra.VOICE_KEYPHRASE_UNENROLL"; + /** + * Intent extra: The hint text to be shown on the voice keyphrase management UI. + */ + public static final String EXTRA_VOICE_KEYPHRASE_HINT_TEXT = + "com.android.intent.extra.VOICE_KEYPHRASE_HINT_TEXT"; + /** + * Intent extra: The voice locale to use while managing the keyphrase. + */ + public static final String EXTRA_VOICE_KEYPHRASE_LOCALE = + "com.android.intent.extra.VOICE_KEYPHRASE_LOCALE"; + + private KeyphraseInfo[] mKeyphrases; + private String mEnrollmentPackage; + private String mParseError; + + public KeyphraseEnrollmentInfo(PackageManager pm) { + // Find the apps that supports enrollment for hotword keyhphrases, + // Pick a privileged app and obtain the information about the supported keyphrases + // from its metadata. + List<ResolveInfo> ris = pm.queryIntentActivities( + new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY); + if (ris == null || ris.isEmpty()) { + // No application capable of enrolling for voice keyphrases is present. + mParseError = "No enrollment application found"; + return; + } + + boolean found = false; + ApplicationInfo ai = null; + for (ResolveInfo ri : ris) { + try { + ai = pm.getApplicationInfo( + ri.activityInfo.packageName, PackageManager.GET_META_DATA); + if ((ai.flags & ApplicationInfo.FLAG_PRIVILEGED) == 0) { + // The application isn't privileged (/system/priv-app). + // The enrollment application needs to be a privileged system app. + Slog.w(TAG, ai.packageName + "is not a privileged system app"); + continue; + } + if (!Manifest.permission.MANAGE_VOICE_KEYPHRASES.equals(ai.permission)) { + // The application trying to manage keyphrases doesn't + // require the MANAGE_VOICE_KEYPHRASES permission. + Slog.w(TAG, ai.packageName + " does not require MANAGE_VOICE_KEYPHRASES"); + continue; + } + mEnrollmentPackage = ai.packageName; + found = true; + break; + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "error parsing voice enrollment meta-data", e); + } + } + + if (!found) { + mKeyphrases = null; + mParseError = "No suitable enrollment application found"; + return; + } + + XmlResourceParser parser = null; + try { + parser = ai.loadXmlMetaData(pm, VOICE_KEYPHRASE_META_DATA); + if (parser == null) { + mParseError = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for " + + ai.packageName; + return; + } + + Resources res = pm.getResourcesForApplication(ai); + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + + String nodeName = parser.getName(); + if (!"voice-enrollment-application".equals(nodeName)) { + mParseError = "Meta-data does not start with voice-enrollment-application tag"; + return; + } + + TypedArray array = res.obtainAttributes(attrs, + com.android.internal.R.styleable.VoiceEnrollmentApplication); + int searchKeyphraseId = array.getInt( + com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId, + -1); + if (searchKeyphraseId != -1) { + String searchKeyphrase = array.getString(com.android.internal.R.styleable + .VoiceEnrollmentApplication_searchKeyphrase); + String searchKeyphraseSupportedLocales = + array.getString(com.android.internal.R.styleable + .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales); + String[] supportedLocales = new String[0]; + // Get all the supported locales from the comma-delimted string. + if (searchKeyphraseSupportedLocales != null + && !searchKeyphraseSupportedLocales.isEmpty()) { + supportedLocales = searchKeyphraseSupportedLocales.split(","); + } + mKeyphrases = new KeyphraseInfo[1]; + mKeyphrases[0] = new KeyphraseInfo( + searchKeyphraseId, searchKeyphrase, supportedLocales); + } else { + mParseError = "searchKeyphraseId not specified in meta-data"; + return; + } + } catch (XmlPullParserException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } catch (IOException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } catch (PackageManager.NameNotFoundException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } finally { + if (parser != null) parser.close(); + } + } + + public String getParseError() { + return mParseError; + } + + /** + * @return An array of available keyphrases that can be enrolled on the system. + * It may be null if no keyphrases can be enrolled. + */ + public KeyphraseInfo[] getKeyphrases() { + return mKeyphrases; + } + + /** + * Returns an intent to launch an activity that manages the given keyphrase + * for the locale. + * + * @param enroll Indicates if the intent should enroll the user or un-enroll them. + * @param keyphrase The keyphrase that the user needs to be enrolled to. + * @param locale The locale for which the enrollment needs to be performed. + * @return An {@link Intent} to manage the keyphrase. This can be null if managing the + * given keyphrase/locale combination isn't possible. + */ + public Intent getManageKeyphraseIntent(boolean enroll, String keyphrase, String locale) { + if (mEnrollmentPackage == null || mEnrollmentPackage.isEmpty()) { + Slog.w(TAG, "No enrollment application exists"); + return null; + } + + if (isKeyphraseEnrollmentSupported(keyphrase, locale)) { + Intent intent = new Intent(ACTION_MANAGE_VOICE_KEYPHRASES) + .setPackage(mEnrollmentPackage) + .putExtra(EXTRA_VOICE_KEYPHRASE_HINT_TEXT, keyphrase) + .putExtra(EXTRA_VOICE_KEYPHRASE_LOCALE, locale); + if (!enroll) intent.putExtra(EXTRA_VOICE_KEYPHRASE_UNENROLL, true); + return intent; + } + return null; + } + + /** + * Indicates if enrollment is supported for the given keyphrase & locale. + * + * @param keyphrase The keyphrase that the user needs to be enrolled to. + * @param locale The locale for which the enrollment needs to be performed. + * @return true, if an enrollment client supports the given keyphrase and the given locale. + */ + public boolean isKeyphraseEnrollmentSupported(String keyphrase, String locale) { + if (mKeyphrases == null || mKeyphrases.length == 0) { + Slog.w(TAG, "Enrollment application doesn't support keyphrases"); + return false; + } + for (KeyphraseInfo keyphraseInfo : mKeyphrases) { + // Check if the given keyphrase is supported in the locale provided by + // the enrollment application. + String supportedKeyphrase = keyphraseInfo.keyphrase; + if (supportedKeyphrase.equalsIgnoreCase(keyphrase) + && keyphraseInfo.supportedLocales.contains(locale)) { + return true; + } + } + Slog.w(TAG, "Enrollment application doesn't support the given keyphrase"); + return false; + } +} diff --git a/core/java/android/service/voice/KeyphraseInfo.java b/core/java/android/service/voice/KeyphraseInfo.java new file mode 100644 index 0000000..d266e1a --- /dev/null +++ b/core/java/android/service/voice/KeyphraseInfo.java @@ -0,0 +1,27 @@ +package android.service.voice; + +import android.util.ArraySet; + +/** + * A Voice Keyphrase. + * @hide + */ +public class KeyphraseInfo { + public final int id; + public final String keyphrase; + public final ArraySet<String> supportedLocales; + + public KeyphraseInfo(int id, String keyphrase, String[] supportedLocales) { + this.id = id; + this.keyphrase = keyphrase; + this.supportedLocales = new ArraySet<String>(supportedLocales.length); + for (String locale : supportedLocales) { + this.supportedLocales.add(locale); + } + } + + @Override + public String toString() { + return "id=" + id + ", keyphrase=" + keyphrase + ", supported-locales=" + supportedLocales; + } +} diff --git a/core/java/android/service/voice/SoundTriggerManager.java b/core/java/android/service/voice/SoundTriggerManager.java new file mode 100644 index 0000000..2d049b9 --- /dev/null +++ b/core/java/android/service/voice/SoundTriggerManager.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.voice; + +import android.hardware.soundtrigger.SoundTrigger; +import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; + +import java.util.ArrayList; + +/** + * Manager for {@link SoundTrigger} APIs. + * Currently this just acts as an abstraction over all SoundTrigger API calls. + * @hide + */ +public class SoundTriggerManager { + /** The {@link DspInfo} for the system, or null if none exists. */ + public DspInfo dspInfo; + + public SoundTriggerManager() { + ArrayList <ModuleProperties> modules = new ArrayList<>(); + int status = SoundTrigger.listModules(modules); + if (status != SoundTrigger.STATUS_OK || modules.size() == 0) { + // TODO(sansid, elaurent): Figure out how to handle errors in listing the modules here. + dspInfo = null; + } else { + // TODO(sansid, elaurent): Figure out how to determine which module corresponds to the + // DSP hardware. + ModuleProperties properties = modules.get(0); + dspInfo = new DspInfo(properties.uuid, properties.implementor, properties.description, + properties.version, properties.powerConsumptionMw); + } + } + + /** + * @return True, if the keyphrase is supported on DSP for the given locale. + */ + public boolean isKeyphraseSupported(String keyphrase, String locale) { + // TODO(sansid): We also need to look into a SoundTrigger API that let's us + // query this. For now just return supported if there's a DSP available. + return dspInfo != null; + } + + /** + * @return True, if the keyphrase is has been enrolled for the given locale. + */ + public boolean isKeyphraseEnrolled(String keyphrase, String locale) { + // TODO(sansid, elaurent): Query SoundTrigger to list currently loaded sound models. + // They have been enrolled. + return false; + } + + /** + * @return True, if a recognition for the keyphrase is active for the given locale. + */ + public boolean isKeyphraseActive(String keyphrase, String locale) { + // TODO(sansid, elaurent): Check if the recognition for the keyphrase is currently active. + return false; + } +} diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index e15489b..e0329f8 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -17,7 +17,6 @@ package android.service.voice; import android.annotation.SdkConstant; -import android.app.Instrumentation; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -25,8 +24,11 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; + +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractionManagerService; + /** * Top-level service of the current global voice interactor, which is providing * support for hotwording, the back-end of a {@link android.app.VoiceInteractor}, etc. @@ -51,6 +53,16 @@ public class VoiceInteractionService extends Service { public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService"; + // TODO(sansid): Unhide these. + /** @hide */ + public static final int KEYPHRASE_UNAVAILABLE = 0; + /** @hide */ + public static final int KEYPHRASE_UNENROLLED = 1; + /** @hide */ + public static final int KEYPHRASE_ENROLLED = 2; + /** @hide */ + public static final int KEYPHRASE_ACTIVE = 3; + /** * Name under which a VoiceInteractionService component publishes information about itself. * This meta-data should reference an XML resource containing a @@ -64,6 +76,9 @@ public class VoiceInteractionService extends Service { IVoiceInteractionManagerService mSystemService; + private SoundTriggerManager mSoundTriggerManager; + private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo; + public void startSession(Bundle args) { try { mSystemService.startSession(mInterface, args); @@ -76,6 +91,8 @@ public class VoiceInteractionService extends Service { super.onCreate(); mSystemService = IVoiceInteractionManagerService.Stub.asInterface( ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); + mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager()); + mSoundTriggerManager = new SoundTriggerManager(); } @Override @@ -85,4 +102,44 @@ public class VoiceInteractionService extends Service { } return null; } + + /** + * Gets the state of always-on hotword detection for the given keyphrase and locale + * on this system. + * Availability implies that the hardware on this system is capable of listening for + * the given keyphrase or not. + * The return code is one of {@link #KEYPHRASE_UNAVAILABLE}, {@link #KEYPHRASE_UNENROLLED} + * {@link #KEYPHRASE_ENROLLED} or {@link #KEYPHRASE_ACTIVE}. + * + * @param keyphrase The keyphrase whose availability is being checked. + * @param locale The locale for which the availability is being checked. + * @return Indicates if always-on hotword detection is available for the given keyphrase. + * TODO(sansid): Unhide this. + * @hide + */ + public final int getAlwaysOnKeyphraseAvailability(String keyphrase, String locale) { + // The available keyphrases is a combination of DSP availability and + // the keyphrases that have an enrollment application for them. + if (!mSoundTriggerManager.isKeyphraseSupported(keyphrase, locale) + || !mKeyphraseEnrollmentInfo.isKeyphraseEnrollmentSupported(keyphrase, locale)) { + return KEYPHRASE_UNAVAILABLE; + } + if (!mSoundTriggerManager.isKeyphraseEnrolled(keyphrase, locale)) { + return KEYPHRASE_UNENROLLED; + } + if (!mSoundTriggerManager.isKeyphraseActive(keyphrase, locale)) { + return KEYPHRASE_ENROLLED; + } else { + return KEYPHRASE_ACTIVE; + } + } + + /** + * @return Details of keyphrases available for enrollment. + * @hide + */ + @VisibleForTesting + protected final KeyphraseEnrollmentInfo getKeyphraseEnrollmentInfo() { + return mKeyphraseEnrollmentInfo; + } } diff --git a/core/java/android/speech/tts/RequestConfig.java b/core/java/android/speech/tts/RequestConfig.java index 4b5385f..84880c0 100644 --- a/core/java/android/speech/tts/RequestConfig.java +++ b/core/java/android/speech/tts/RequestConfig.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ package android.speech.tts; import android.media.AudioManager; diff --git a/core/java/android/speech/tts/RequestConfigHelper.java b/core/java/android/speech/tts/RequestConfigHelper.java index b25c985..3b5490b 100644 --- a/core/java/android/speech/tts/RequestConfigHelper.java +++ b/core/java/android/speech/tts/RequestConfigHelper.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ package android.speech.tts; import android.speech.tts.TextToSpeechClient.EngineStatus; diff --git a/core/java/android/speech/tts/SynthesisRequestV2.java b/core/java/android/speech/tts/SynthesisRequestV2.java index 130e3f9..a42aa16 100644 --- a/core/java/android/speech/tts/SynthesisRequestV2.java +++ b/core/java/android/speech/tts/SynthesisRequestV2.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ package android.speech.tts; import android.os.Bundle; diff --git a/core/java/android/speech/tts/VoiceInfo.java b/core/java/android/speech/tts/VoiceInfo.java index 16b9a97..71629dc 100644 --- a/core/java/android/speech/tts/VoiceInfo.java +++ b/core/java/android/speech/tts/VoiceInfo.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ package android.speech.tts; import android.os.Bundle; diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index f440853..1d9aa05 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -440,26 +440,10 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable } // Documentation from interface - public SpannableStringBuilder replace(int start, int end, + public SpannableStringBuilder replace(final int start, final int end, CharSequence tb, int tbstart, int tbend) { checkRange("replace", start, end); - // Sanity check - if (start > end) { - Log.w(TAG, "Bad arguments to #replace : " - + "start = " + start + ", end = " + end); - final int tmp = start; - start = end; - end = tmp; - } - if (tbstart > tbend) { - Log.w(TAG, "Bad arguments to #replace : " - + "tbstart = " + tbstart + ", tbend = " + tbend); - final int tmp = tbstart; - tbstart = tbend; - tbend = tmp; - } - int filtercount = mFilters.length; for (int i = 0; i < filtercount; i++) { CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, this, start, end); diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java index deb138d..c1341e1 100644 --- a/core/java/android/text/util/Linkify.java +++ b/core/java/android/text/util/Linkify.java @@ -465,32 +465,39 @@ public class Linkify { String address; int base = 0; - while ((address = WebView.findAddress(string)) != null) { - int start = string.indexOf(address); + try { + while ((address = WebView.findAddress(string)) != null) { + int start = string.indexOf(address); - if (start < 0) { - break; - } + if (start < 0) { + break; + } - LinkSpec spec = new LinkSpec(); - int length = address.length(); - int end = start + length; - - spec.start = base + start; - spec.end = base + end; - string = string.substring(end); - base += end; - - String encodedAddress = null; - - try { - encodedAddress = URLEncoder.encode(address,"UTF-8"); - } catch (UnsupportedEncodingException e) { - continue; - } + LinkSpec spec = new LinkSpec(); + int length = address.length(); + int end = start + length; - spec.url = "geo:0,0?q=" + encodedAddress; - links.add(spec); + spec.start = base + start; + spec.end = base + end; + string = string.substring(end); + base += end; + + String encodedAddress = null; + + try { + encodedAddress = URLEncoder.encode(address,"UTF-8"); + } catch (UnsupportedEncodingException e) { + continue; + } + + spec.url = "geo:0,0?q=" + encodedAddress; + links.add(spec); + } + } catch (UnsupportedOperationException e) { + // findAddress may fail with an unsupported exception on platforms without a WebView. + // In this case, we will not append anything to the links variable: it would have died + // in WebView.findAddress. + return; } } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index e9c2bba..0a4f641 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -603,76 +603,76 @@ public abstract class Transition implements Cloneable { for (int i = 0; i < startValuesList.size(); ++i) { TransitionValues start = startValuesList.get(i); TransitionValues end = endValuesList.get(i); - // Only bother trying to animate with values that differ between start/end - if (start != null || end != null) { - if (start == null || !start.equals(end)) { - if (DBG) { - View view = (end != null) ? end.view : start.view; - Log.d(LOG_TAG, " differing start/end values for view " + - view); - if (start == null || end == null) { - Log.d(LOG_TAG, " " + ((start == null) ? - "start null, end non-null" : "start non-null, end null")); - } else { - for (String key : start.values.keySet()) { - Object startValue = start.values.get(key); - Object endValue = end.values.get(key); - if (startValue != endValue && !startValue.equals(endValue)) { - Log.d(LOG_TAG, " " + key + ": start(" + startValue + - "), end(" + endValue +")"); - } + // Only bother trying to animate with valid values that differ between start/end + boolean isInvalidStart = start != null && !isValidTarget(start.view); + boolean isInvalidEnd = end != null && !isValidTarget(end.view); + boolean isChanged = start != end && (start == null || !start.equals(end)); + if (isChanged && !isInvalidStart && !isInvalidEnd) { + if (DBG) { + View view = (end != null) ? end.view : start.view; + Log.d(LOG_TAG, " differing start/end values for view " + view); + if (start == null || end == null) { + Log.d(LOG_TAG, " " + ((start == null) ? + "start null, end non-null" : "start non-null, end null")); + } else { + for (String key : start.values.keySet()) { + Object startValue = start.values.get(key); + Object endValue = end.values.get(key); + if (startValue != endValue && !startValue.equals(endValue)) { + Log.d(LOG_TAG, " " + key + ": start(" + startValue + + "), end(" + endValue + ")"); } } } - // TODO: what to do about targetIds and itemIds? - Animator animator = createAnimator(sceneRoot, start, end); - if (animator != null) { - // Save animation info for future cancellation purposes - View view = null; - TransitionValues infoValues = null; - if (end != null) { - view = end.view; - String[] properties = getTransitionProperties(); - if (view != null && properties != null && properties.length > 0) { - infoValues = new TransitionValues(); - infoValues.view = view; - TransitionValues newValues = endValues.viewValues.get(view); - if (newValues != null) { - for (int j = 0; j < properties.length; ++j) { - infoValues.values.put(properties[j], - newValues.values.get(properties[j])); - } + } + // TODO: what to do about targetIds and itemIds? + Animator animator = createAnimator(sceneRoot, start, end); + if (animator != null) { + // Save animation info for future cancellation purposes + View view = null; + TransitionValues infoValues = null; + if (end != null) { + view = end.view; + String[] properties = getTransitionProperties(); + if (view != null && properties != null && properties.length > 0) { + infoValues = new TransitionValues(); + infoValues.view = view; + TransitionValues newValues = endValues.viewValues.get(view); + if (newValues != null) { + for (int j = 0; j < properties.length; ++j) { + infoValues.values.put(properties[j], + newValues.values.get(properties[j])); } - int numExistingAnims = runningAnimators.size(); - for (int j = 0; j < numExistingAnims; ++j) { - Animator anim = runningAnimators.keyAt(j); - AnimationInfo info = runningAnimators.get(anim); - if (info.values != null && info.view == view && - ((info.name == null && getName() == null) || - info.name.equals(getName()))) { - if (info.values.equals(infoValues)) { - // Favor the old animator - animator = null; - break; - } + } + int numExistingAnims = runningAnimators.size(); + for (int j = 0; j < numExistingAnims; ++j) { + Animator anim = runningAnimators.keyAt(j); + AnimationInfo info = runningAnimators.get(anim); + if (info.values != null && info.view == view && + ((info.name == null && getName() == null) || + info.name.equals(getName()))) { + if (info.values.equals(infoValues)) { + // Favor the old animator + animator = null; + break; } } } - } else { - view = (start != null) ? start.view : null; } - if (animator != null) { - if (mPropagation != null) { - long delay = mPropagation - .getStartDelay(sceneRoot, this, start, end); - startDelays.put(mAnimators.size(), delay); - minStartDelay = Math.min(delay, minStartDelay); - } - AnimationInfo info = new AnimationInfo(view, getName(), - sceneRoot.getWindowId(), infoValues); - runningAnimators.put(animator, info); - mAnimators.add(animator); + } else { + view = (start != null) ? start.view : null; + } + if (animator != null) { + if (mPropagation != null) { + long delay = mPropagation + .getStartDelay(sceneRoot, this, start, end); + startDelays.put(mAnimators.size(), delay); + minStartDelay = Math.min(delay, minStartDelay); } + AnimationInfo info = new AnimationInfo(view, getName(), + sceneRoot.getWindowId(), infoValues); + runningAnimators.put(animator, info); + mAnimators.add(animator); } } } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index c32a2c9..fa5bd88 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -46,7 +46,7 @@ interface IWindowSession { int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outContentInsets); void remove(IWindow window); - + /** * Change the parameters of a window. You supply the * new parameters, it returns the new frame of the window on screen (the @@ -109,7 +109,7 @@ interface IWindowSession { * to optimize compositing of this part of the window. */ void setTransparentRegion(IWindow window, in Region region); - + /** * Tell the window manager about the content and visible insets of the * given window, which can be used to adjust the <var>outContentInsets</var> @@ -122,20 +122,20 @@ interface IWindowSession { */ void setInsets(IWindow window, int touchableInsets, in Rect contentInsets, in Rect visibleInsets, in Region touchableRegion); - + /** * Return the current display size in which the window is being laid out, * accounting for screen decorations around it. */ void getDisplayFrame(IWindow window, out Rect outDisplayFrame); - + void finishDrawing(IWindow window); - + void setInTouchMode(boolean showFocus); boolean getInTouchMode(); - + boolean performHapticFeedback(IWindow window, int effectId, boolean always); - + /** * Allocate the drag's thumbnail surface. Also assigns a token that identifies * the drag to the OS and passes that as the return value. A return value of @@ -150,11 +150,11 @@ interface IWindowSession { boolean performDrag(IWindow window, IBinder dragToken, float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data); - /** - * Report the result of a drop action targeted to the given window. - * consumed is 'true' when the drop was accepted by a valid recipient, - * 'false' otherwise. - */ + /** + * Report the result of a drop action targeted to the given window. + * consumed is 'true' when the drop was accepted by a valid recipient, + * 'false' otherwise. + */ void reportDropResult(IWindow window, boolean consumed); /** @@ -174,12 +174,12 @@ interface IWindowSession { * how big the increment is from one screen to another. */ void setWallpaperPosition(IBinder windowToken, float x, float y, float xstep, float ystep); - + void wallpaperOffsetsComplete(IBinder window); - + Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, int z, in Bundle extras, boolean sync); - + void wallpaperCommandComplete(IBinder window, in Bundle result); void setUniverseTransform(IBinder window, float alpha, float offx, float offy, @@ -188,7 +188,7 @@ interface IWindowSession { /** * Notifies that a rectangle on the screen has been requested. */ - void onRectangleOnScreenRequested(IBinder token, in Rect rectangle, boolean immediate); + void onRectangleOnScreenRequested(IBinder token, in Rect rectangle); IWindowId getWindowId(IBinder window); } diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 4631b64..c165475 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -842,13 +842,6 @@ public class RenderNode { } /** - * Sets the scroll position, this is used for damage calculations - */ - public void setScrollPosition(int x, int y) { - nSetScrollPosition(mNativeRenderNode, x, y); - } - - /** * Outputs the display list to the log. This method exists for use by * tools to output display lists for selected nodes to the log. * @@ -906,7 +899,6 @@ public class RenderNode { private static native boolean nSetRight(long renderNode, int right); private static native boolean nSetTop(long renderNode, int top); private static native boolean nSetLeft(long renderNode, int left); - private static native void nSetScrollPosition(long renderNode, int scrollX, int scrollY); private static native boolean nSetCameraDistance(long renderNode, float distance); private static native boolean nSetPivotY(long renderNode, float pivotY); private static native boolean nSetPivotX(long renderNode, float pivotX); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 5cd3d62..79f19b5 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -78,8 +78,8 @@ public class SurfaceControl { IBinder displayToken); private static native int nativeGetActiveConfig(IBinder displayToken); private static native boolean nativeSetActiveConfig(IBinder displayToken, int id); - private static native void nativeBlankDisplay(IBinder displayToken); - private static native void nativeUnblankDisplay(IBinder displayToken); + private static native void nativeSetDisplayPowerMode( + IBinder displayToken, int mode); private final CloseGuard mCloseGuard = CloseGuard.get(); @@ -209,6 +209,25 @@ public class SurfaceControl { */ public static final int BUILT_IN_DISPLAY_ID_HDMI = 1; + /* Display power modes * / + + /** + * Display power mode off: used while blanking the screen. + * Use only with {@link SurfaceControl#setDisplayPowerMode()}. + */ + public static final int POWER_MODE_OFF = 0; + + /** + * Display power mode doze: used while putting the screen into low power mode. + * Use only with {@link SurfaceControl#setDisplayPowerMode()}. + */ + public static final int POWER_MODE_DOZE = 1; + + /** + * Display power mode normal: used while unblanking the screen. + * Use only with {@link SurfaceControl#setDisplayPowerMode()}. + */ + public static final int POWER_MODE_NORMAL = 2; /** @@ -487,18 +506,11 @@ public class SurfaceControl { } } - public static void unblankDisplay(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - nativeUnblankDisplay(displayToken); - } - - public static void blankDisplay(IBinder displayToken) { + public static void setDisplayPowerMode(IBinder displayToken, int mode) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - nativeBlankDisplay(displayToken); + nativeSetDisplayPowerMode(displayToken, mode); } public static SurfaceControl.PhysicalDisplayInfo[] getDisplayConfigs(IBinder displayToken) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 65b1f8c..21bae23 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2775,6 +2775,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @hide * + * Whether Recents is visible or not. + */ + public static final int RECENT_APPS_VISIBLE = 0x00004000; + + /** + * @hide + * * Makes system ui transparent. */ public static final int SYSTEM_UI_TRANSPARENT = 0x00008000; @@ -2782,7 +2789,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @hide */ - public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00007FFF; + public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FFF; /** * These are the system UI flags that can be cleared by events outside @@ -6106,7 +6113,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // apply insets path and take things from there. try { mPrivateFlags3 |= PFLAG3_FITTING_SYSTEM_WINDOWS; - return !dispatchApplyWindowInsets(new WindowInsets(insets)).hasInsets(); + return dispatchApplyWindowInsets(new WindowInsets(insets)).isConsumed(); } finally { mPrivateFlags3 &= ~PFLAG3_FITTING_SYSTEM_WINDOWS; } @@ -13738,7 +13745,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } - renderNode.setScrollPosition(mScrollX, mScrollY); if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (!isLayer && mRecreateDisplayList)) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 2905851..b29f6d4 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5600,11 +5600,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @Override public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { insets = super.dispatchApplyWindowInsets(insets); - if (insets.hasInsets()) { + if (!insets.isConsumed()) { final int count = getChildCount(); for (int i = 0; i < count; i++) { insets = getChildAt(i).dispatchApplyWindowInsets(insets); - if (!insets.hasInsets()) { + if (insets.isConsumed()) { break; } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 76d5038..1be0d4e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6215,7 +6215,7 @@ public final class ViewRootImpl implements ViewParent, mTempRect.offset(0, -mCurScrollY); mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop); try { - mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect, immediate); + mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect); } catch (RemoteException re) { /* ignore */ } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index ecc4586..0120875 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1516,6 +1516,29 @@ public abstract class Window { public boolean getAllowExitTransitionOverlap() { return true; } /** + * Returns the duration, in milliseconds, of the window background fade + * when transitioning into or away from an Activity when called with an Activity Transition. + * <p>When executing the enter transition, the background starts transparent + * and fades in. This requires {@link #FEATURE_CONTENT_TRANSITIONS}. The default is + * 300 milliseconds.</p> + * @return The duration of the window background fade to opaque during enter transition. + * @see #getEnterTransition() + */ + public long getTransitionBackgroundFadeDuration() { return 0; } + + /** + * Sets the duration, in milliseconds, of the window background fade + * when transitioning into or away from an Activity when called with an Activity Transition. + * <p>When executing the enter transition, the background starts transparent + * and fades in. This requires {@link #FEATURE_CONTENT_TRANSITIONS}. The default is + * 300 milliseconds.</p> + * @param fadeDurationMillis The duration of the window background fade to or from opaque + * during enter transition. + * @see #setEnterTransition(android.transition.Transition) + */ + public void setTransitionBackgroundFadeDuration(long fadeDurationMillis) { } + + /** * @return the color of the status bar. */ public abstract int getStatusBarColor(); diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java index 7f89044..b721074 100644 --- a/core/java/android/view/WindowInfo.java +++ b/core/java/android/view/WindowInfo.java @@ -111,6 +111,7 @@ public class WindowInfo implements Parcelable { builder.append("type=").append(type); builder.append(", layer=").append(layer); builder.append(", token=").append(token); + builder.append(", bounds=").append(boundsInScreen); builder.append(", parent=").append(parentToken); builder.append(", focused=").append(focused); builder.append(", children=").append(childTokens); diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 57e774e..1d2f1bf 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -35,6 +35,9 @@ public final class WindowInsets { private Rect mTempRect; private boolean mIsRound; + private boolean mSystemWindowInsetsConsumed = false; + private boolean mWindowDecorInsetsConsumed = false; + private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0); /** @@ -43,7 +46,13 @@ public final class WindowInsets { * since it would allow them to inadvertently consume unknown insets by returning it. * @hide */ - public static final WindowInsets EMPTY = new WindowInsets(EMPTY_RECT, EMPTY_RECT); + public static final WindowInsets CONSUMED; + + static { + CONSUMED = new WindowInsets(EMPTY_RECT, EMPTY_RECT); + CONSUMED.mSystemWindowInsetsConsumed = true; + CONSUMED.mWindowDecorInsetsConsumed = true; + } /** @hide */ public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) { @@ -52,13 +61,17 @@ public final class WindowInsets { /** @hide */ public WindowInsets(Rect systemWindowInsets, boolean isRound) { - this(systemWindowInsets, EMPTY_RECT, isRound); + this(systemWindowInsets, null, isRound); } /** @hide */ public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) { - mSystemWindowInsets = systemWindowInsets; - mWindowDecorInsets = windowDecorInsets; + mSystemWindowInsetsConsumed = systemWindowInsets == null; + mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets; + + mWindowDecorInsetsConsumed = windowDecorInsets == null; + mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets; + mIsRound = isRound; } @@ -70,12 +83,14 @@ public final class WindowInsets { public WindowInsets(WindowInsets src) { mSystemWindowInsets = src.mSystemWindowInsets; mWindowDecorInsets = src.mWindowDecorInsets; + mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed; + mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed; mIsRound = src.mIsRound; } /** @hide */ public WindowInsets(Rect systemWindowInsets) { - this(systemWindowInsets, EMPTY_RECT); + this(systemWindowInsets, null); } /** @@ -243,6 +258,24 @@ public final class WindowInsets { } /** + * Check if these insets have been fully consumed. + * + * <p>Insets are considered "consumed" if the applicable <code>consume*</code> methods + * have been called such that all insets have been set to zero. This affects propagation of + * insets through the view hierarchy; insets that have not been fully consumed will continue + * to propagate down to child views.</p> + * + * <p>The result of this method is equivalent to the return value of + * {@link View#fitSystemWindows(android.graphics.Rect)}.</p> + * + * @return true if the insets have been fully consumed. + * @hide Pending API + */ + public boolean isConsumed() { + return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed; + } + + /** * Returns true if the associated window has a round shape. * * <p>A round window's left, top, right and bottom edges reach all the way to the @@ -263,7 +296,8 @@ public final class WindowInsets { */ public WindowInsets consumeSystemWindowInsets() { final WindowInsets result = new WindowInsets(this); - result.mSystemWindowInsets = new Rect(0, 0, 0, 0); + result.mSystemWindowInsets = EMPTY_RECT; + result.mSystemWindowInsetsConsumed = true; return result; } @@ -281,10 +315,12 @@ public final class WindowInsets { boolean right, boolean bottom) { if (left || top || right || bottom) { final WindowInsets result = new WindowInsets(this); - result.mSystemWindowInsets = new Rect(left ? 0 : mSystemWindowInsets.left, + result.mSystemWindowInsets = new Rect( + left ? 0 : mSystemWindowInsets.left, top ? 0 : mSystemWindowInsets.top, right ? 0 : mSystemWindowInsets.right, bottom ? 0 : mSystemWindowInsets.bottom); + result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets(); return result; } return this; @@ -304,6 +340,7 @@ public final class WindowInsets { int right, int bottom) { final WindowInsets result = new WindowInsets(this); result.mSystemWindowInsets = new Rect(left, top, right, bottom); + result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets(); return result; } @@ -313,6 +350,7 @@ public final class WindowInsets { public WindowInsets consumeWindowDecorInsets() { final WindowInsets result = new WindowInsets(this); result.mWindowDecorInsets.set(0, 0, 0, 0); + result.mWindowDecorInsetsConsumed = true; return result; } @@ -327,6 +365,7 @@ public final class WindowInsets { top ? 0 : mWindowDecorInsets.top, right ? 0 : mWindowDecorInsets.right, bottom ? 0 : mWindowDecorInsets.bottom); + result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets(); return result; } return this; @@ -338,6 +377,7 @@ public final class WindowInsets { public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) { final WindowInsets result = new WindowInsets(this); result.mWindowDecorInsets = new Rect(left, top, right, bottom); + result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets(); return result; } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 2b4677c..d426edc 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -605,21 +605,21 @@ public interface WindowManagerPolicy { public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation); /** - * Return whether the given window should forcibly hide everything - * behind it. Typically returns true for the keyguard. + * Return whether the given window is forcibly hiding all windows except windows with + * FLAG_SHOW_WHEN_LOCKED set. Typically returns true for the keyguard. */ - public boolean doesForceHide(WindowManager.LayoutParams attrs); + public boolean isForceHiding(WindowManager.LayoutParams attrs); /** - * Return whether the given window can become one that passes doesForceHide() test. + * Return whether the given window can become one that passes isForceHiding() test. * Typically returns true for the StatusBar. */ public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs); /** * Determine if a window that is behind one that is force hiding - * (as determined by {@link #doesForceHide}) should actually be hidden. + * (as determined by {@link #isForceHiding}) should actually be hidden. * For example, typically returns false for the status bar. Be careful * to return false for any window that you may hide yourself, since this * will conflict with what you set. @@ -830,13 +830,11 @@ public interface WindowManagerPolicy { * setting the window's frame, either here or in finishLayout(). * * @param win The window being positioned. - * @param attrs The LayoutParams of the window. * @param attached For sub-windows, the window it is attached to; this * window will already have had layoutWindow() called on it * so you can use its Rect. Otherwise null. */ - public void layoutWindowLw(WindowState win, - WindowManager.LayoutParams attrs, WindowState attached); + public void layoutWindowLw(WindowState win, WindowState attached); /** diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 5b9372d..4748402 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -225,17 +225,11 @@ public final class AccessibilityInteractionClient try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { - List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows(); - if (windows != null) { - if (DEBUG) { - Log.i(LOG_TAG, "Window cache hit"); - } - return windows; - } - if (DEBUG) { - Log.i(LOG_TAG, "Window cache miss"); - } - windows = connection.getWindows(); + // The system is just sending data for windows that we introspected + // and changed but not ones that appeared, so we have to always call + // into the system process. This is less expensice as opposed to + // sending all windows on every window change. + List<AccessibilityWindowInfo> windows = connection.getWindows(); if (windows != null) { final int windowCount = windows.size(); for (int i = 0; i < windowCount; i++) { diff --git a/core/java/android/view/animation/AccelerateInterpolator.java b/core/java/android/view/animation/AccelerateInterpolator.java index c08f348..1c75f16 100644 --- a/core/java/android/view/animation/AccelerateInterpolator.java +++ b/core/java/android/view/animation/AccelerateInterpolator.java @@ -17,15 +17,18 @@ package android.view.animation; import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.util.AttributeSet; +import com.android.internal.R; import com.android.internal.view.animation.HasNativeInterpolator; import com.android.internal.view.animation.NativeInterpolatorFactory; import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; /** - * An interpolator where the rate of change starts out slowly and + * An interpolator where the rate of change starts out slowly and * and then accelerates. * */ @@ -38,10 +41,10 @@ public class AccelerateInterpolator implements Interpolator, NativeInterpolatorF mFactor = 1.0f; mDoubleFactor = 2.0; } - + /** * Constructor - * + * * @param factor Degree to which the animation should be eased. Seting * factor to 1.0f produces a y=x^2 parabola. Increasing factor above * 1.0f exaggerates the ease-in effect (i.e., it starts even @@ -51,17 +54,26 @@ public class AccelerateInterpolator implements Interpolator, NativeInterpolatorF mFactor = factor; mDoubleFactor = 2 * mFactor; } - + public AccelerateInterpolator(Context context, AttributeSet attrs) { - TypedArray a = - context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AccelerateInterpolator); - - mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f); + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.AccelerateInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.AccelerateInterpolator); + } + + mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f); mDoubleFactor = 2 * mFactor; a.recycle(); } - + public float getInterpolation(float input) { if (mFactor == 1.0f) { return input * input; diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 1d1fa1e..af4e04f 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -20,6 +20,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.XmlResourceParser; import android.content.res.Resources.NotFoundException; import android.util.AttributeSet; @@ -143,7 +145,7 @@ public class AnimationUtils { */ public static LayoutAnimationController loadLayoutAnimation(Context context, int id) throws NotFoundException { - + XmlResourceParser parser = null; try { parser = context.getResources().getAnimation(id); @@ -201,7 +203,7 @@ public class AnimationUtils { /** * Make an animation for objects becoming visible. Uses a slide and fade * effect. - * + * * @param c Context for loading resources * @param fromLeft is the object to be animated coming from the left * @return The new animation @@ -218,11 +220,11 @@ public class AnimationUtils { a.setStartTime(currentAnimationTimeMillis()); return a; } - + /** * Make an animation for objects becoming invisible. Uses a slide and fade * effect. - * + * * @param c Context for loading resources * @param toRight is the object to be animated exiting to the right * @return The new animation @@ -234,17 +236,17 @@ public class AnimationUtils { } else { a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_left); } - + a.setInterpolator(new AccelerateInterpolator()); a.setStartTime(currentAnimationTimeMillis()); return a; } - + /** * Make an animation for objects becoming visible. Uses a slide up and fade * effect. - * + * * @param c Context for loading resources * @return The new animation */ @@ -255,10 +257,10 @@ public class AnimationUtils { a.setStartTime(currentAnimationTimeMillis()); return a; } - + /** * Loads an {@link Interpolator} object from a resource - * + * * @param context Application context used to access resources * @param id The resource id of the animation to load * @return The animation object reference by the specified id @@ -268,7 +270,7 @@ public class AnimationUtils { XmlResourceParser parser = null; try { parser = context.getResources().getAnimation(id); - return createInterpolatorFromXml(context, parser); + return createInterpolatorFromXml(context.getResources(), context.getTheme(), parser); } catch (XmlPullParserException ex) { NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + Integer.toHexString(id)); @@ -284,54 +286,84 @@ public class AnimationUtils { } } - - private static Interpolator createInterpolatorFromXml(Context c, XmlPullParser parser) + + /** + * Loads an {@link Interpolator} object from a resource + * + * @param res The resources + * @param id The resource id of the animation to load + * @return The interpolator object reference by the specified id + * @throws NotFoundException + * @hide + */ + public static Interpolator loadInterpolator(Resources res, Theme theme, int id) throws NotFoundException { + XmlResourceParser parser = null; + try { + parser = res.getAnimation(id); + return createInterpolatorFromXml(res, theme, parser); + } catch (XmlPullParserException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } catch (IOException ex) { + NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" + + Integer.toHexString(id)); + rnf.initCause(ex); + throw rnf; + } finally { + if (parser != null) + parser.close(); + } + + } + + private static Interpolator createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser) throws XmlPullParserException, IOException { - + Interpolator interpolator = null; - + // Make sure we are on a start tag. int type; int depth = parser.getDepth(); - while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) - && type != XmlPullParser.END_DOCUMENT) { + while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) + && type != XmlPullParser.END_DOCUMENT) { if (type != XmlPullParser.START_TAG) { continue; } AttributeSet attrs = Xml.asAttributeSet(parser); - - String name = parser.getName(); - - + + String name = parser.getName(); + if (name.equals("linearInterpolator")) { - interpolator = new LinearInterpolator(c, attrs); + interpolator = new LinearInterpolator(); } else if (name.equals("accelerateInterpolator")) { - interpolator = new AccelerateInterpolator(c, attrs); + interpolator = new AccelerateInterpolator(res, theme, attrs); } else if (name.equals("decelerateInterpolator")) { - interpolator = new DecelerateInterpolator(c, attrs); - } else if (name.equals("accelerateDecelerateInterpolator")) { - interpolator = new AccelerateDecelerateInterpolator(c, attrs); - } else if (name.equals("cycleInterpolator")) { - interpolator = new CycleInterpolator(c, attrs); + interpolator = new DecelerateInterpolator(res, theme, attrs); + } else if (name.equals("accelerateDecelerateInterpolator")) { + interpolator = new AccelerateDecelerateInterpolator(); + } else if (name.equals("cycleInterpolator")) { + interpolator = new CycleInterpolator(res, theme, attrs); } else if (name.equals("anticipateInterpolator")) { - interpolator = new AnticipateInterpolator(c, attrs); + interpolator = new AnticipateInterpolator(res, theme, attrs); } else if (name.equals("overshootInterpolator")) { - interpolator = new OvershootInterpolator(c, attrs); + interpolator = new OvershootInterpolator(res, theme, attrs); } else if (name.equals("anticipateOvershootInterpolator")) { - interpolator = new AnticipateOvershootInterpolator(c, attrs); + interpolator = new AnticipateOvershootInterpolator(res, theme, attrs); } else if (name.equals("bounceInterpolator")) { - interpolator = new BounceInterpolator(c, attrs); + interpolator = new BounceInterpolator(); } else if (name.equals("pathInterpolator")) { - interpolator = new PathInterpolator(c, attrs); + interpolator = new PathInterpolator(res, theme, attrs); } else { throw new RuntimeException("Unknown interpolator name: " + parser.getName()); } } - + return interpolator; } diff --git a/core/java/android/view/animation/AnticipateInterpolator.java b/core/java/android/view/animation/AnticipateInterpolator.java index 83a8007..fe756bd 100644 --- a/core/java/android/view/animation/AnticipateInterpolator.java +++ b/core/java/android/view/animation/AnticipateInterpolator.java @@ -17,9 +17,12 @@ package android.view.animation; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.Resources.Theme; import android.util.AttributeSet; +import com.android.internal.R; import com.android.internal.view.animation.HasNativeInterpolator; import com.android.internal.view.animation.NativeInterpolatorFactory; import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; @@ -45,11 +48,20 @@ public class AnticipateInterpolator implements Interpolator, NativeInterpolatorF } public AnticipateInterpolator(Context context, AttributeSet attrs) { - TypedArray a = context.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.AnticipateInterpolator); + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public AnticipateInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.AnticipateInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.AnticipateInterpolator); + } mTension = - a.getFloat(com.android.internal.R.styleable.AnticipateInterpolator_tension, 2.0f); + a.getFloat(R.styleable.AnticipateInterpolator_tension, 2.0f); a.recycle(); } diff --git a/core/java/android/view/animation/AnticipateOvershootInterpolator.java b/core/java/android/view/animation/AnticipateOvershootInterpolator.java index 1a8adfd..78e5acf 100644 --- a/core/java/android/view/animation/AnticipateOvershootInterpolator.java +++ b/core/java/android/view/animation/AnticipateOvershootInterpolator.java @@ -17,6 +17,8 @@ package android.view.animation; import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.util.AttributeSet; @@ -62,7 +64,17 @@ public class AnticipateOvershootInterpolator implements Interpolator, NativeInte } public AnticipateOvershootInterpolator(Context context, AttributeSet attrs) { - TypedArray a = context.obtainStyledAttributes(attrs, AnticipateOvershootInterpolator); + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public AnticipateOvershootInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, AnticipateOvershootInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, AnticipateOvershootInterpolator); + } mTension = a.getFloat(AnticipateOvershootInterpolator_tension, 2.0f) * a.getFloat(AnticipateOvershootInterpolator_extraTension, 1.5f); diff --git a/core/java/android/view/animation/CycleInterpolator.java b/core/java/android/view/animation/CycleInterpolator.java index d1ebf05..3114aa3 100644 --- a/core/java/android/view/animation/CycleInterpolator.java +++ b/core/java/android/view/animation/CycleInterpolator.java @@ -17,9 +17,12 @@ package android.view.animation; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.Resources.Theme; import android.util.AttributeSet; +import com.android.internal.R; import com.android.internal.view.animation.HasNativeInterpolator; import com.android.internal.view.animation.NativeInterpolatorFactory; import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; @@ -34,20 +37,29 @@ public class CycleInterpolator implements Interpolator, NativeInterpolatorFactor public CycleInterpolator(float cycles) { mCycles = cycles; } - + public CycleInterpolator(Context context, AttributeSet attrs) { - TypedArray a = - context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.CycleInterpolator); - - mCycles = a.getFloat(com.android.internal.R.styleable.CycleInterpolator_cycles, 1.0f); - + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public CycleInterpolator(Resources resources, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.CycleInterpolator, 0, 0); + } else { + a = resources.obtainAttributes(attrs, R.styleable.CycleInterpolator); + } + + mCycles = a.getFloat(R.styleable.CycleInterpolator_cycles, 1.0f); + a.recycle(); } - + public float getInterpolation(float input) { return (float)(Math.sin(2 * mCycles * Math.PI * input)); } - + private float mCycles; /** @hide */ diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java index 0789a0e..674207c 100644 --- a/core/java/android/view/animation/DecelerateInterpolator.java +++ b/core/java/android/view/animation/DecelerateInterpolator.java @@ -17,15 +17,18 @@ package android.view.animation; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.Resources.Theme; import android.util.AttributeSet; +import com.android.internal.R; import com.android.internal.view.animation.HasNativeInterpolator; import com.android.internal.view.animation.NativeInterpolatorFactory; import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; /** - * An interpolator where the rate of change starts out quickly and + * An interpolator where the rate of change starts out quickly and * and then decelerates. * */ @@ -36,7 +39,7 @@ public class DecelerateInterpolator implements Interpolator, NativeInterpolatorF /** * Constructor - * + * * @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces * an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the * ease-out effect (i.e., it starts even faster and ends evens slower) @@ -44,16 +47,25 @@ public class DecelerateInterpolator implements Interpolator, NativeInterpolatorF public DecelerateInterpolator(float factor) { mFactor = factor; } - + public DecelerateInterpolator(Context context, AttributeSet attrs) { - TypedArray a = - context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.DecelerateInterpolator); - - mFactor = a.getFloat(com.android.internal.R.styleable.DecelerateInterpolator_factor, 1.0f); - + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public DecelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.DecelerateInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.DecelerateInterpolator); + } + + mFactor = a.getFloat(R.styleable.DecelerateInterpolator_factor, 1.0f); + a.recycle(); } - + public float getInterpolation(float input) { float result; if (mFactor == 1.0f) { @@ -63,7 +75,7 @@ public class DecelerateInterpolator implements Interpolator, NativeInterpolatorF } return result; } - + private float mFactor = 1.0f; /** @hide */ diff --git a/core/java/android/view/animation/OvershootInterpolator.java b/core/java/android/view/animation/OvershootInterpolator.java index a2466f1..d6c2808 100644 --- a/core/java/android/view/animation/OvershootInterpolator.java +++ b/core/java/android/view/animation/OvershootInterpolator.java @@ -17,9 +17,12 @@ package android.view.animation; import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.util.AttributeSet; +import com.android.internal.R; import com.android.internal.view.animation.HasNativeInterpolator; import com.android.internal.view.animation.NativeInterpolatorFactory; import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; @@ -46,11 +49,20 @@ public class OvershootInterpolator implements Interpolator, NativeInterpolatorFa } public OvershootInterpolator(Context context, AttributeSet attrs) { - TypedArray a = context.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.OvershootInterpolator); + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public OvershootInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.OvershootInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.OvershootInterpolator); + } mTension = - a.getFloat(com.android.internal.R.styleable.OvershootInterpolator_tension, 2.0f); + a.getFloat(R.styleable.OvershootInterpolator_tension, 2.0f); a.recycle(); } diff --git a/core/java/android/view/animation/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java index a369509..da12ffb 100644 --- a/core/java/android/view/animation/PathInterpolator.java +++ b/core/java/android/view/animation/PathInterpolator.java @@ -16,11 +16,15 @@ package android.view.animation; import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.Theme; import android.content.res.TypedArray; import android.graphics.Path; import android.util.AttributeSet; import android.view.InflateException; +import com.android.internal.R; + /** * An interpolator that can traverse a Path that extends from <code>Point</code> * <code>(0, 0)</code> to <code>(1, 1)</code>. The x coordinate along the <code>Path</code> @@ -81,18 +85,33 @@ public class PathInterpolator implements Interpolator { } public PathInterpolator(Context context, AttributeSet attrs) { - TypedArray a = context.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.PathInterpolator); - if (!a.hasValue(com.android.internal.R.styleable.PathInterpolator_controlX1)) { + this(context.getResources(), context.getTheme(), attrs); + } + + /** @hide */ + public PathInterpolator(Resources res, Theme theme, AttributeSet attrs) { + TypedArray a; + if (theme != null) { + a = theme.obtainStyledAttributes(attrs, R.styleable.PathInterpolator, 0, 0); + } else { + a = res.obtainAttributes(attrs, R.styleable.PathInterpolator); + } + parseInterpolatorFromTypeArray(a); + + a.recycle(); + } + + private void parseInterpolatorFromTypeArray(TypedArray a) { + if (!a.hasValue(R.styleable.PathInterpolator_controlX1)) { throw new InflateException("pathInterpolator requires the controlX1 attribute"); - } else if (!a.hasValue(com.android.internal.R.styleable.PathInterpolator_controlY1)) { + } else if (!a.hasValue(R.styleable.PathInterpolator_controlY1)) { throw new InflateException("pathInterpolator requires the controlY1 attribute"); } - float x1 = a.getFloat(com.android.internal.R.styleable.PathInterpolator_controlX1, 0); - float y1 = a.getFloat(com.android.internal.R.styleable.PathInterpolator_controlY1, 0); + float x1 = a.getFloat(R.styleable.PathInterpolator_controlX1, 0); + float y1 = a.getFloat(R.styleable.PathInterpolator_controlY1, 0); - boolean hasX2 = a.hasValue(com.android.internal.R.styleable.PathInterpolator_controlX2); - boolean hasY2 = a.hasValue(com.android.internal.R.styleable.PathInterpolator_controlY2); + boolean hasX2 = a.hasValue(R.styleable.PathInterpolator_controlX2); + boolean hasY2 = a.hasValue(R.styleable.PathInterpolator_controlY2); if (hasX2 != hasY2) { throw new InflateException( @@ -102,12 +121,10 @@ public class PathInterpolator implements Interpolator { if (!hasX2) { initQuad(x1, y1); } else { - float x2 = a.getFloat(com.android.internal.R.styleable.PathInterpolator_controlX2, 0); - float y2 = a.getFloat(com.android.internal.R.styleable.PathInterpolator_controlY2, 0); + float x2 = a.getFloat(R.styleable.PathInterpolator_controlX2, 0); + float y2 = a.getFloat(R.styleable.PathInterpolator_controlY2, 0); initCubic(x1, y1, x2, y2); } - - a.recycle(); } private void initQuad(float controlX, float controlY) { diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java index fad6747..66f5f6c 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfo.java +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -186,12 +186,12 @@ public final class CursorAnchorInfo implements Parcelable { /** * Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe. */ - public static final class CursorAnchorInfoBuilder { + public static final class Builder { /** * Sets the text range of the selection. Calling this can be skipped if there is no * selection. */ - public CursorAnchorInfoBuilder setSelectionRange(final int newStart, final int newEnd) { + public Builder setSelectionRange(final int newStart, final int newEnd) { mSelectionStart = newStart; mSelectionEnd = newEnd; return this; @@ -205,8 +205,7 @@ public final class CursorAnchorInfo implements Parcelable { * @param index index where the composing text starts. * @param composingText the entire composing text. */ - public CursorAnchorInfoBuilder setComposingText(final int index, - final CharSequence composingText) { + public Builder setComposingText(final int index, final CharSequence composingText) { mComposingTextStart = index; if (composingText == null) { mComposingText = null; @@ -236,9 +235,8 @@ public final class CursorAnchorInfo implements Parcelable { * that will be transformed with the transformation matrix when rendered on the screen. This * should be calculated or compatible with {@link Layout#getLineBottom(int)}. */ - public CursorAnchorInfoBuilder setInsertionMarkerLocation( - final float horizontalPosition, final float lineTop, final float lineBaseline, - final float lineBottom){ + public Builder setInsertionMarkerLocation(final float horizontalPosition, + final float lineTop, final float lineBaseline, final float lineBottom){ mInsertionMarkerHorizontal = horizontalPosition; mInsertionMarkerTop = lineTop; mInsertionMarkerBaseline = lineBaseline; @@ -269,9 +267,8 @@ public final class CursorAnchorInfo implements Parcelable { * @throws IllegalArgumentException If the index is a negative value, or not greater than * all of the previously called indices. */ - public CursorAnchorInfoBuilder addCharacterRect(final int index, - final float leadingEdgeX, final float leadingEdgeY, final float trailingEdgeX, - final float trailingEdgeY) { + public Builder addCharacterRect(final int index, final float leadingEdgeX, + final float leadingEdgeY, final float trailingEdgeX, final float trailingEdgeY) { if (index < 0) { throw new IllegalArgumentException("index must not be a negative integer."); } @@ -289,7 +286,7 @@ public final class CursorAnchorInfo implements Parcelable { * @param matrix transformation matrix from local coordinates into screen coordinates. null * is interpreted as an identity matrix. */ - public CursorAnchorInfoBuilder setMatrix(final Matrix matrix) { + public Builder setMatrix(final Matrix matrix) { mMatrix.set(matrix != null ? matrix : Matrix.IDENTITY_MATRIX); return this; } @@ -297,7 +294,7 @@ public final class CursorAnchorInfo implements Parcelable { /** * @return {@link CursorAnchorInfo} using parameters in this - * {@link CursorAnchorInfoBuilder}. + * {@link Builder}. */ public CursorAnchorInfo build() { return new CursorAnchorInfo(this); @@ -323,7 +320,7 @@ public final class CursorAnchorInfo implements Parcelable { } } - private CursorAnchorInfo(final CursorAnchorInfoBuilder builder) { + private CursorAnchorInfo(final Builder builder) { mSelectionStart = builder.mSelectionStart; mSelectionEnd = builder.mSelectionEnd; mComposingTextStart = builder.mComposingTextStart; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 27d6b82..4467128 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -96,7 +96,6 @@ import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.CursorAnchorInfo; -import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -3013,7 +3012,7 @@ public class Editor { * {@link InputMethodManager#isWatchingCursor(View)} returns false. */ private final class CursorAnchorInfoNotifier implements TextViewPositionListener { - final CursorAnchorInfoBuilder mSelectionInfoBuilder = new CursorAnchorInfoBuilder(); + final CursorAnchorInfo.Builder mSelectionInfoBuilder = new CursorAnchorInfo.Builder(); final int[] mTmpIntOffset = new int[2]; final Matrix mViewToScreenMatrix = new Matrix(); @@ -3037,7 +3036,7 @@ public class Editor { return; } - final CursorAnchorInfoBuilder builder = mSelectionInfoBuilder; + final CursorAnchorInfo.Builder builder = mSelectionInfoBuilder; builder.reset(); final int selectionStart = mTextView.getSelectionStart(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 43c8dde..84202eb 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5842,7 +5842,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int end = text.partialEndOffset; if (end > N) end = N; removeParcelableSpans(content, start, end); - // If start > end, content.replace will swap them before using them. content.replace(start, end, text.text); } } diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index cbd9a6a..4f1cd68 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -146,6 +146,8 @@ public class Toolbar extends ViewGroup { private ActionMenuPresenter mOuterActionMenuPresenter; private ExpandedActionViewMenuPresenter mExpandedMenuPresenter; + private boolean mCollapsible; + public Toolbar(Context context) { this(context, null); } @@ -969,6 +971,23 @@ public class Toolbar extends ViewGroup { return child.getMeasuredWidth() + hMargins; } + /** + * Returns true if the Toolbar is collapsible and has no child views with a measured size > 0. + */ + private boolean shouldCollapse() { + if (!mCollapsible) return false; + + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + if (shouldLayout(child) && child.getMeasuredWidth() > 0 && + child.getMeasuredHeight() > 0) { + return false; + } + } + return true; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = 0; @@ -1093,7 +1112,8 @@ public class Toolbar extends ViewGroup { final int measuredHeight = resolveSizeAndState( Math.max(height, getSuggestedMinimumHeight()), heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT); - setMeasuredDimension(measuredWidth, measuredHeight); + + setMeasuredDimension(measuredWidth, shouldCollapse() ? 0 : measuredHeight); } @Override @@ -1490,6 +1510,16 @@ public class Toolbar extends ViewGroup { } /** + * Force the toolbar to collapse to zero-height during measurement if + * it could be considered "empty" (no visible elements with nonzero measured size) + * @hide + */ + public void setCollapsible(boolean collapsible) { + mCollapsible = collapsible; + requestLayout(); + } + + /** * Interface responsible for receiving menu item click events if the items themselves * do not have individual item click listeners. */ diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index 8a9cb22..ea36e37 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Rect; @@ -246,6 +247,13 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar } @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + init(getContext()); + requestApplyInsets(); + } + + @Override public void onWindowSystemUiVisibilityChanged(int visible) { super.onWindowSystemUiVisibilityChanged(visible); pullChildren(); @@ -329,7 +337,7 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar // insets in all cases, we need to know the measured size of the various action // bar elements. onApplyWindowInsets() happens before the measure pass, so we can't // do that here. Instead we will take this up in onMeasure(). - return WindowInsets.EMPTY; + return WindowInsets.CONSUMED; } @Override diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java index b298d85..7fb2efd 100644 --- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java +++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java @@ -424,7 +424,7 @@ public class ToolbarWidgetWrapper implements DecorToolbar { @Override public void setCollapsible(boolean collapsible) { - // Ignore + mToolbar.setCollapsible(collapsible); } @Override @@ -480,7 +480,7 @@ public class ToolbarWidgetWrapper implements DecorToolbar { private void ensureSpinner() { if (mSpinner == null) { - mSpinner = new Spinner(getContext()); + mSpinner = new Spinner(getContext(), null, R.attr.actionDropDownStyle); Toolbar.LayoutParams lp = new Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL); mSpinner.setLayoutParams(lp); @@ -569,7 +569,7 @@ public class ToolbarWidgetWrapper implements DecorToolbar { @Override public void setNavigationIcon(int resId) { - setNavigationIcon(mToolbar.getContext().getDrawable(resId)); + setNavigationIcon(resId != 0 ? mToolbar.getContext().getDrawable(resId) : null); } @Override diff --git a/core/jni/Android.mk b/core/jni/Android.mk index de46804..15dfed1 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -89,6 +89,7 @@ LOCAL_SRC_FILES:= \ android_util_Process.cpp \ android_util_StringBlock.cpp \ android_util_XmlBlock.cpp \ + android/graphics/AndroidPicture.cpp \ android/graphics/AutoDecodeCancel.cpp \ android/graphics/Bitmap.cpp \ android/graphics/BitmapFactory.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f8e6bc3..f2b9bac 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -244,9 +244,6 @@ AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) mArgBlockLength(argBlockLength) { SkGraphics::Init(); - // this sets our preference for 16bit images during decode - // in case the src is opaque and 24bit - SkImageDecoder::SetDeviceConfig(SkBitmap::kRGB_565_Config); // There is also a global font cache, but its budget is specified in code // see SkFontHost_android.cpp @@ -825,8 +822,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) mOptions.add(opt); } - // Whether the profile should start upon app startup or be delayed by some random offset. - property_get("dalvik.vm.profile.start-immediately", propBuf, "0"); + // Whether the profile should start upon app startup or be delayed by some random offset + // (in seconds) that is bound between 0 and a fixed value. + property_get("dalvik.vm.profile.start-immed", propBuf, "0"); if (propBuf[0] == '1') { opt.optionString = "-Xprofile-start-immediately"; mOptions.add(opt); diff --git a/core/jni/android/graphics/AndroidPicture.cpp b/core/jni/android/graphics/AndroidPicture.cpp new file mode 100644 index 0000000..5977ab2 --- /dev/null +++ b/core/jni/android/graphics/AndroidPicture.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 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. + */ + +#include "AndroidPicture.h" +#include "SkCanvas.h" +#include "SkStream.h" + +AndroidPicture::AndroidPicture(const AndroidPicture* src) { + if (NULL != src) { + mWidth = src->width(); + mHeight = src->height(); + if (NULL != src->mPicture.get()) { + mPicture.reset(SkRef(src->mPicture.get())); + } if (NULL != src->mRecorder.get()) { + mPicture.reset(src->makePartialCopy()); + } + } else { + mWidth = 0; + mHeight = 0; + } +} + +SkCanvas* AndroidPicture::beginRecording(int width, int height) { + mPicture.reset(NULL); + mRecorder.reset(new SkPictureRecorder); + mWidth = width; + mHeight = height; + return mRecorder->beginRecording(width, height, NULL, 0); +} + +void AndroidPicture::endRecording() { + if (NULL != mRecorder.get()) { + mPicture.reset(mRecorder->endRecording()); + mRecorder.reset(NULL); + } +} + +int AndroidPicture::width() const { + if (NULL != mPicture.get()) { + SkASSERT(mPicture->width() == mWidth); + SkASSERT(mPicture->height() == mHeight); + } + + return mWidth; +} + +int AndroidPicture::height() const { + if (NULL != mPicture.get()) { + SkASSERT(mPicture->width() == mWidth); + SkASSERT(mPicture->height() == mHeight); + } + + return mHeight; +} + +AndroidPicture* AndroidPicture::CreateFromStream(SkStream* stream) { + AndroidPicture* newPict = new AndroidPicture; + + newPict->mPicture.reset(SkPicture::CreateFromStream(stream)); + if (NULL != newPict->mPicture.get()) { + newPict->mWidth = newPict->mPicture->width(); + newPict->mHeight = newPict->mPicture->height(); + } + + return newPict; +} + +void AndroidPicture::serialize(SkWStream* stream) const { + if (NULL != mRecorder.get()) { + SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy()); + tempPict->serialize(stream); + } else if (NULL != mPicture.get()) { + mPicture->serialize(stream); + } else { + SkPicture empty; + empty.serialize(stream); + } +} + +void AndroidPicture::draw(SkCanvas* canvas) { + if (NULL != mRecorder.get()) { + this->endRecording(); + SkASSERT(NULL != mPicture.get()); + } + if (NULL != mPicture.get()) { + // TODO: remove this const_cast once pictures are immutable + const_cast<SkPicture*>(mPicture.get())->draw(canvas); + } +} + +SkPicture* AndroidPicture::makePartialCopy() const { + SkASSERT(NULL != mRecorder.get()); + + SkPictureRecorder reRecorder; + + SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0); + mRecorder->partialReplay(canvas); + return reRecorder.endRecording(); +} diff --git a/core/jni/android/graphics/AndroidPicture.h b/core/jni/android/graphics/AndroidPicture.h new file mode 100644 index 0000000..f434941 --- /dev/null +++ b/core/jni/android/graphics/AndroidPicture.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef ANDROID_PICTURE_H +#define ANDROID_PICTURE_H + +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkRefCnt.h" +#include "SkTemplates.h" + +class SkCanvas; +class SkPicture; +class SkPictureRecorder; +class SkStream; +class SkWStream; + +// Skia's SkPicture class has been split into an SkPictureRecorder +// and an SkPicture. AndroidPicture recreates the functionality +// of the old SkPicture interface by flip-flopping between the two +// new classes. +class AndroidPicture { +public: + explicit AndroidPicture(const AndroidPicture* src = NULL); + + SkCanvas* beginRecording(int width, int height); + + void endRecording(); + + int width() const; + + int height() const; + + static AndroidPicture* CreateFromStream(SkStream* stream); + + void serialize(SkWStream* stream) const; + + void draw(SkCanvas* canvas); + +private: + int mWidth; + int mHeight; + SkAutoTUnref<const SkPicture> mPicture; + SkAutoTDelete<SkPictureRecorder> mRecorder; + + // Make a copy of a picture that is in the midst of being recorded. The + // resulting picture will have balanced saves and restores. + SkPicture* makePartialCopy() const; +}; +#endif // ANDROID_PICTURE_H diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 0328517..9998995 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -361,24 +361,50 @@ static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { } static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, - jint width, jint height, jint configHandle, jint allocSize) { + jint width, jint height, jint configHandle, jint allocSize, + jboolean requestPremul) { SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle); - if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) { + SkColorType colorType = SkBitmapConfigToColorType(config); + + // ARGB_4444 is a deprecated format, convert automatically to 8888 + if (colorType == kARGB_4444_SkColorType) { + colorType = kN32_SkColorType; + } + + if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) { // done in native as there's no way to get BytesPerPixel in Java doThrowIAE(env, "Bitmap not large enough to support new configuration"); return; } SkPixelRef* ref = bitmap->pixelRef(); - SkSafeRef(ref); - bitmap->setConfig(config, width, height); + ref->ref(); + SkAlphaType alphaType; + if (bitmap->colorType() != kRGB_565_SkColorType + && bitmap->alphaType() == kOpaque_SkAlphaType) { + // If the original bitmap was set to opaque, keep that setting, unless it + // was 565, which is required to be opaque. + alphaType = kOpaque_SkAlphaType; + } else { + // Otherwise respect the premultiplied request. + alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; + } + bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); + // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for + // its alphatype), so it would make more sense from Skia's perspective to create a + // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key + // for its cache, so it won't realize this is the same Java Bitmap. + SkImageInfo& info = const_cast<SkImageInfo&>(ref->info()); + // Use the updated from the SkBitmap, which may have corrected an invalid alphatype. + // (e.g. 565 non-opaque) + info = bitmap->info(); bitmap->setPixelRef(ref); // notifyPixelsChanged will increment the generation ID even though the actual pixel data // hasn't been touched. This signals the renderer that the bitmap (including width, height, - // and config) has changed. + // colortype and alphatype) has changed. ref->notifyPixelsChanged(); - SkSafeUnref(ref); + ref->unref(); } // These must match the int values in Bitmap.java @@ -799,7 +825,7 @@ static JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_copy }, { "nativeDestructor", "(J)V", (void*)Bitmap_destructor }, { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle }, - { "nativeReconfigure", "(JIIII)V", (void*)Bitmap_reconfigure }, + { "nativeReconfigure", "(JIIIIZ)V", (void*)Bitmap_reconfigure }, { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", (void*)Bitmap_compress }, { "nativeErase", "(JI)V", (void*)Bitmap_erase }, diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 3b44f97..6de3b9e 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -19,11 +19,14 @@ #include <android_runtime/AndroidRuntime.h> #include "SkCanvas.h" +#include "SkClipStack.h" #include "SkDevice.h" +#include "SkDeque.h" #include "SkDrawFilter.h" #include "SkGraphics.h" #include "SkPorterDuff.h" #include "SkShader.h" +#include "SkTArray.h" #include "SkTemplates.h" #ifdef USE_MINIKIN @@ -43,11 +46,42 @@ namespace android { +class ClipCopier : public SkCanvas::ClipVisitor { +public: + ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {} + + virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipRect(rect, op, antialias); + } + virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipRRect(rrect, op, antialias); + } + virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipPath(path, op, antialias); + } + +private: + SkCanvas* m_dstCanvas; +}; + // Holds an SkCanvas reference plus additional native data. class NativeCanvasWrapper { +private: + struct SaveRec { + int saveCount; + SkCanvas::SaveFlags saveFlags; + }; + public: NativeCanvasWrapper(SkCanvas* canvas) - : mCanvas(canvas) { } + : mCanvas(canvas) + , mSaveStack(NULL) { + SkASSERT(canvas); + } + + ~NativeCanvasWrapper() { + delete mSaveStack; + } SkCanvas* getCanvas() const { return mCanvas.get(); @@ -56,28 +90,127 @@ public: void setCanvas(SkCanvas* canvas) { SkASSERT(canvas); mCanvas.reset(canvas); - } -private: - SkAutoTUnref<SkCanvas> mCanvas; -}; + delete mSaveStack; + mSaveStack = NULL; + } -class ClipCopier : public SkCanvas::ClipVisitor { -public: - ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {} + int save(SkCanvas::SaveFlags flags) { + int count = mCanvas->save(); + recordPartialSave(flags); + return count; + } - virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) { - m_dstCanvas->clipRect(rect, op, antialias); + int saveLayer(const SkRect* bounds, const SkPaint* paint, + SkCanvas::SaveFlags flags) { + int count = mCanvas->saveLayer(bounds, paint, + static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag)); + recordPartialSave(flags); + return count; } - virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) { - m_dstCanvas->clipRRect(rrect, op, antialias); + + int saveLayerAlpha(const SkRect* bounds, U8CPU alpha, + SkCanvas::SaveFlags flags) { + int count = mCanvas->saveLayerAlpha(bounds, alpha, + static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag)); + recordPartialSave(flags); + return count; } - virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) { - m_dstCanvas->clipPath(path, op, antialias); + + void restore() { + const SaveRec* rec = (NULL == mSaveStack) + ? NULL + : static_cast<SaveRec*>(mSaveStack->back()); + int currentSaveCount = mCanvas->getSaveCount() - 1; + SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount); + + if (NULL == rec || rec->saveCount != currentSaveCount) { + // Fast path - no record for this frame. + mCanvas->restore(); + return; + } + + bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag); + bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag); + + SkMatrix savedMatrix; + if (preserveMatrix) { + savedMatrix = mCanvas->getTotalMatrix(); + } + + SkTArray<SkClipStack::Element> savedClips; + if (preserveClip) { + saveClipsForFrame(savedClips, currentSaveCount); + } + + mCanvas->restore(); + + if (preserveMatrix) { + mCanvas->setMatrix(savedMatrix); + } + + if (preserveClip && !savedClips.empty()) { + applyClips(savedClips); + } + + mSaveStack->pop_back(); } private: - SkCanvas* m_dstCanvas; + void recordPartialSave(SkCanvas::SaveFlags flags) { + // A partial save is a save operation which doesn't capture the full canvas state. + // (either kMatrix_SaveFlags or kClip_SaveFlag is missing). + + // Mask-out non canvas state bits. + flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag); + + if (SkCanvas::kMatrixClip_SaveFlag == flags) { + // not a partial save. + return; + } + + if (NULL == mSaveStack) { + mSaveStack = new SkDeque(sizeof(struct SaveRec), 8); + } + + SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back()); + // Store the save counter in the SkClipStack domain. + // (0-based, equal to the number of save ops on the stack). + rec->saveCount = mCanvas->getSaveCount() - 1; + rec->saveFlags = flags; + } + + void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, + int frameSaveCount) { + SkClipStack::Iter clipIterator(*mCanvas->getClipStack(), + SkClipStack::Iter::kTop_IterStart); + while (const SkClipStack::Element* elem = clipIterator.next()) { + if (elem->getSaveCount() < frameSaveCount) { + // done with the current frame. + break; + } + SkASSERT(elem->getSaveCount() == frameSaveCount); + clips.push_back(*elem); + } + } + + void applyClips(const SkTArray<SkClipStack::Element>& clips) { + ClipCopier clipCopier(mCanvas); + + // The clip stack stores clips in device space. + SkMatrix origMatrix = mCanvas->getTotalMatrix(); + mCanvas->resetMatrix(); + + // We pushed the clips in reverse order. + for (int i = clips.count() - 1; i >= 0; --i) { + clips[i].replay(&clipCopier); + } + + mCanvas->setMatrix(origMatrix); + } + + SkAutoTUnref<SkCanvas> mCanvas; + SkDeque* mSaveStack; // lazily allocated, tracks partial saves. }; // Returns true if the SkCanvas's clip is non-empty. @@ -88,11 +221,15 @@ static jboolean hasNonEmptyClip(const SkCanvas& canvas) { class SkCanvasGlue { public: + // Get the native wrapper for a given handle. + static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) { + SkASSERT(nativeHandle); + return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle); + } // Get the SkCanvas for a given native handle. static inline SkCanvas* getNativeCanvas(jlong nativeHandle) { - SkASSERT(nativeHandle); - NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle); + NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle); SkCanvas* canvas = wrapper->getCanvas(); SkASSERT(canvas); @@ -186,56 +323,56 @@ public: } static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); - return static_cast<jint>(canvas->save(flags)); + return static_cast<jint>(wrapper->save(flags)); } static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t, jfloat r, jfloat b, - jlong paintHandle, jint flags) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); + jlong paintHandle, jint flagsHandle) { + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); SkRect bounds; bounds.set(l, t, r, b); - int result = canvas->saveLayer(&bounds, paint, - static_cast<SkCanvas::SaveFlags>(flags)); - return static_cast<jint>(result); + return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags)); } static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t, jfloat r, jfloat b, - jint alpha, jint flags) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); + jint alpha, jint flagsHandle) { + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); + SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); SkRect bounds; bounds.set(l, t, r, b); - int result = canvas->saveLayerAlpha(&bounds, alpha, - static_cast<SkCanvas::SaveFlags>(flags)); - return static_cast<jint>(result); + return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags)); } static void restore(JNIEnv* env, jobject, jlong canvasHandle) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); - if (canvas->getSaveCount() <= 1) { // cannot restore anymore + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); + if (wrapper->getCanvas()->getSaveCount() <= 1) { // cannot restore anymore doThrowISE(env, "Underflow in restore"); return; } - canvas->restore(); + wrapper->restore(); } static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); - return static_cast<jint>(canvas->getSaveCount()); + return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount()); } static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); if (restoreCount < 1) { doThrowIAE(env, "Underflow in restoreToCount"); return; } - canvas->restoreToCount(restoreCount); + + while (wrapper->getCanvas()->getSaveCount() > restoreCount) { + wrapper->restore(); + } } static void translate(JNIEnv*, jobject, jlong canvasHandle, diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 64ad223..a4337cc 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -3,6 +3,7 @@ #include "jni.h" #include "JNIHelp.h" #include "GraphicsJNI.h" +#include "AndroidPicture.h" #include "SkCanvas.h" #include "SkDevice.h" @@ -345,13 +346,13 @@ android::TypefaceImpl* GraphicsJNI::getNativeTypeface(JNIEnv* env, jobject paint return p; } -SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) +AndroidPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) { SkASSERT(env); SkASSERT(picture); SkASSERT(env->IsInstanceOf(picture, gPicture_class)); jlong pictureHandle = env->GetLongField(picture, gPicture_nativeInstanceID); - SkPicture* p = reinterpret_cast<SkPicture*>(pictureHandle); + AndroidPicture* p = reinterpret_cast<AndroidPicture*>(pictureHandle); SkASSERT(p); return p; } diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 73dd11b..2e2f920 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -14,7 +14,7 @@ class SkBitmapRegionDecoder; class SkCanvas; class SkPaint; -class SkPicture; +class AndroidPicture; class GraphicsJNI { public: @@ -50,7 +50,7 @@ public: static SkPaint* getNativePaint(JNIEnv*, jobject paint); static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint); static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap); - static SkPicture* getNativePicture(JNIEnv*, jobject picture); + static AndroidPicture* getNativePicture(JNIEnv*, jobject picture); static SkRegion* getNativeRegion(JNIEnv*, jobject region); // Given the 'native' long held by the Rasterizer.java object, return a diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp index a8a3dae..0683f73 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/core/jni/android/graphics/Picture.cpp @@ -17,9 +17,9 @@ #include "jni.h" #include "GraphicsJNI.h" #include <android_runtime/AndroidRuntime.h> +#include "AndroidPicture.h" #include "SkCanvas.h" -#include "SkPicture.h" #include "SkStream.h" #include "SkTemplates.h" #include "CreateJavaOutputStreamAdaptor.h" @@ -29,45 +29,41 @@ namespace android { class SkPictureGlue { public: static jlong newPicture(JNIEnv* env, jobject, jlong srcHandle) { - const SkPicture* src = reinterpret_cast<SkPicture*>(srcHandle); - if (src) { - return reinterpret_cast<jlong>(new SkPicture(*src)); - } else { - return reinterpret_cast<jlong>(new SkPicture); - } + const AndroidPicture* src = reinterpret_cast<AndroidPicture*>(srcHandle); + return reinterpret_cast<jlong>(new AndroidPicture(src)); } static jlong deserialize(JNIEnv* env, jobject, jobject jstream, jbyteArray jstorage) { - SkPicture* picture = NULL; + AndroidPicture* picture = NULL; SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); if (strm) { - picture = SkPicture::CreateFromStream(strm); + picture = AndroidPicture::CreateFromStream(strm); delete strm; } return reinterpret_cast<jlong>(picture); } static void killPicture(JNIEnv* env, jobject, jlong pictureHandle) { - SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle); + AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); SkASSERT(picture); - picture->unref(); + delete picture; } static void draw(JNIEnv* env, jobject, jlong canvasHandle, jlong pictureHandle) { SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); - SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle); + AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); SkASSERT(canvas); SkASSERT(picture); picture->draw(canvas); } static jboolean serialize(JNIEnv* env, jobject, jlong pictureHandle, - jobject jstream, jbyteArray jstorage) { - SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle); + jobject jstream, jbyteArray jstorage) { + AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); - + if (NULL != strm) { picture->serialize(strm); delete strm; @@ -78,19 +74,21 @@ public: static jint getWidth(JNIEnv* env, jobject jpic) { NPE_CHECK_RETURN_ZERO(env, jpic); - int width = GraphicsJNI::getNativePicture(env, jpic)->width(); + AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic); + int width = pict->width(); return static_cast<jint>(width); } static jint getHeight(JNIEnv* env, jobject jpic) { NPE_CHECK_RETURN_ZERO(env, jpic); - int height = GraphicsJNI::getNativePicture(env, jpic)->height(); + AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic); + int height = pict->height(); return static_cast<jint>(height); } static jlong beginRecording(JNIEnv* env, jobject, jlong pictHandle, - jint w, jint h) { - SkPicture* pict = reinterpret_cast<SkPicture*>(pictHandle); + jint w, jint h) { + AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle); // beginRecording does not ref its return value, it just returns it. SkCanvas* canvas = pict->beginRecording(w, h); // the java side will wrap this guy in a Canvas.java, which will call @@ -101,7 +99,7 @@ public: } static void endRecording(JNIEnv* env, jobject, jlong pictHandle) { - SkPicture* pict = reinterpret_cast<SkPicture*>(pictHandle); + AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle); pict->endRecording(); } }; diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp index d54aaa8..3812c27 100644 --- a/core/jni/android/graphics/pdf/PdfDocument.cpp +++ b/core/jni/android/graphics/pdf/PdfDocument.cpp @@ -24,6 +24,7 @@ #include "SkCanvas.h" #include "SkDocument.h" #include "SkPicture.h" +#include "SkPictureRecorder.h" #include "SkStream.h" #include "SkRect.h" @@ -32,15 +33,22 @@ namespace android { struct PageRecord { PageRecord(int width, int height, const SkRect& contentRect) - : mPicture(new SkPicture()), mWidth(width), mHeight(height) { + : mPictureRecorder(new SkPictureRecorder()) + , mPicture(NULL) + , mWidth(width) + , mHeight(height) { mContentRect = contentRect; } ~PageRecord() { - mPicture->unref(); + delete mPictureRecorder; + if (NULL != mPicture) { + mPicture->unref(); + } } - SkPicture* const mPicture; + SkPictureRecorder* mPictureRecorder; + SkPicture* mPicture; const int mWidth; const int mHeight; SkRect mContentRect; @@ -62,8 +70,8 @@ public: mPages.push_back(page); mCurrentPage = page; - SkCanvas* canvas = page->mPicture->beginRecording( - contentRect.width(), contentRect.height(), 0); + SkCanvas* canvas = page->mPictureRecorder->beginRecording( + contentRect.width(), contentRect.height(), NULL, 0); // We pass this canvas to Java where it is used to construct // a Java Canvas object which dereferences the pointer when it @@ -75,7 +83,11 @@ public: void finishPage() { assert(mCurrentPage != NULL); - mCurrentPage->mPicture->endRecording(); + assert(mCurrentPage->mPictureRecorder != NULL); + assert(mCurrentPage->mPicture == NULL); + mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->endRecording(); + delete mCurrentPage->mPictureRecorder; + mCurrentPage->mPictureRecorder = NULL; mCurrentPage = NULL; } @@ -89,7 +101,7 @@ public: canvas->clipRect(page->mContentRect); canvas->translate(page->mContentRect.left(), page->mContentRect.top()); - canvas->drawPicture(*page->mPicture); + canvas->drawPicture(page->mPicture); document->endPage(); } @@ -97,11 +109,10 @@ public: } void close() { + assert(NULL == mCurrentPage); for (unsigned i = 0; i < mPages.size(); i++) { delete mPages[i]; } - delete mCurrentPage; - mCurrentPage = NULL; } private: diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index bf47dd3..ee4c619 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -244,6 +244,12 @@ android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source) } static jint +android_media_AudioSystem_newAudioSessionId(JNIEnv *env, jobject thiz) +{ + return AudioSystem::newAudioSessionId(); +} + +static jint android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs) { const jchar* c_keyValuePairs = env->GetStringCritical(keyValuePairs, 0); @@ -1295,6 +1301,7 @@ static JNINativeMethod gMethods[] = { {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive}, {"isStreamActiveRemotely","(II)Z", (void *)android_media_AudioSystem_isStreamActiveRemotely}, {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive}, + {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId}, {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState}, {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState}, {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState}, diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index e5f79f2..26f8993 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -275,13 +275,6 @@ static jboolean android_view_RenderNode_offsetTopAndBottom(JNIEnv* env, return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y); } -static void android_view_RenderNode_setScrollPosition(JNIEnv* env, - jobject clazz, jlong renderNodePtr, jint scrollX, jint scrollY) { - RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); - SET_AND_DIRTY(setScrollX, scrollX, RenderNode::GENERIC); - SET_AND_DIRTY(setScrollY, scrollY, RenderNode::GENERIC); -} - // ---------------------------------------------------------------------------- // RenderProperties - getters // ---------------------------------------------------------------------------- @@ -517,7 +510,6 @@ static JNINativeMethod gMethods[] = { { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom }, { "nOffsetLeftAndRight", "(JF)Z", (void*) android_view_RenderNode_offsetLeftAndRight }, { "nOffsetTopAndBottom", "(JF)Z", (void*) android_view_RenderNode_offsetTopAndBottom }, - { "nSetScrollPosition", "(JII)V", (void*) android_view_RenderNode_setScrollPosition }, { "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering }, { "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline }, diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 11f87cc..3d14aaf 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -95,6 +95,7 @@ sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) { env->GetLongField(surfaceObj, gSurfaceClassInfo.mNativeObject)); env->MonitorExit(lock); } + env->DeleteLocalRef(lock); return sur; } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 8e6c4a0..c0d5221 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -412,20 +412,12 @@ static jboolean nativeSetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenOb return err == NO_ERROR ? JNI_TRUE : JNI_FALSE; } -static void nativeBlankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { +static void nativeSetDisplayPowerMode(JNIEnv* env, jclass clazz, jobject tokenObj, jint mode) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return; - ALOGD_IF_SLOW(100, "Excessive delay in blankDisplay() while turning screen off"); - SurfaceComposerClient::blankDisplay(token); -} - -static void nativeUnblankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return; - - ALOGD_IF_SLOW(100, "Excessive delay in unblankDisplay() while turning screen on"); - SurfaceComposerClient::unblankDisplay(token); + ALOGD_IF_SLOW(100, "Excessive delay in setPowerMode()"); + SurfaceComposerClient::setDisplayPowerMode(token, mode); } static jboolean nativeClearContentFrameStats(JNIEnv* env, jclass clazz, jlong nativeObject) { @@ -628,10 +620,6 @@ static JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetActiveConfig }, {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveConfig }, - {"nativeBlankDisplay", "(Landroid/os/IBinder;)V", - (void*)nativeBlankDisplay }, - {"nativeUnblankDisplay", "(Landroid/os/IBinder;)V", - (void*)nativeUnblankDisplay }, {"nativeClearContentFrameStats", "(J)Z", (void*)nativeClearContentFrameStats }, {"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z", @@ -640,6 +628,8 @@ static JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeClearAnimationFrameStats }, {"nativeGetAnimationFrameStats", "(Landroid/view/WindowAnimationFrameStats;)Z", (void*)nativeGetAnimationFrameStats }, + {"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V", + (void*)nativeSetDisplayPowerMode }, }; int register_android_view_SurfaceControl(JNIEnv* env) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 28d4487..3067cdd0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1770,11 +1770,11 @@ <!-- Allows the system to bind to an application's task services @hide --> - <permission android:name="android.permission.BIND_TASK_SERVICE" + <permission android:name="android.permission.BIND_JOB_SERVICE" android:protectionLevel="signature" - android:label="@string/permlab_bindTaskService" - android:description="@string/permdesc_bindTaskService" /> - <uses-permission android:name="android.permission.BIND_TASK_SERVICE"/> + android:label="@string/permlab_bindJobService" + android:description="@string/permdesc_bindJobService" /> + <uses-permission android:name="android.permission.BIND_JOB_SERVICE"/> <!-- ========================================= --> <!-- Permissions for special development tools --> @@ -2071,6 +2071,14 @@ android:description="@string/permdesc_bindVoiceInteraction" android:protectionLevel="signature" /> + <!-- Must be required by hotword enrollment application, + to ensure that only the system can interact with it. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES" + android:label="@string/permlab_manageVoiceKeyphrases" + android:description="@string/permdesc_manageVoiceKeyphrases" + android:protectionLevel="signature|system" /> + <!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider}, to ensure that only the system can bind to it. @hide --> @@ -2699,7 +2707,8 @@ android:theme="@style/Theme.Holo.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" - android:multiprocess="true"> + android:multiprocess="true" + android:documentLaunchMode="never"> <intent-filter> <action android:name="android.intent.action.CHOOSER" /> <category android:name="android.intent.category.DEFAULT" /> @@ -2721,7 +2730,7 @@ </activity-alias> <activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile" android:targetActivity="com.android.internal.app.IntentForwarderActivity" - android:icon="@drawable/work_icon" + android:icon="@drawable/ic_afw_icon" android:exported="true" android:label="@string/managed_profile_label"> </activity-alias> @@ -2805,7 +2814,7 @@ </activity> <activity android:name="com.android.internal.app.RestrictionsPinActivity" - android:theme="@style/Theme.Holo.Dialog.Alert" + android:theme="@style/Theme.Material.Light.Dialog.Alert" android:excludeFromRecents="true" android:windowSoftInputMode="adjustPan" android:process=":ui"> @@ -2888,8 +2897,8 @@ </service> <service android:name="com.android.server.MountServiceIdler" - android:exported="false" - android:permission="android.permission.BIND_TASK_SERVICE" > + android:exported="true" + android:permission="android.permission.BIND_JOB_SERVICE" > </service> </application> diff --git a/core/res/res/anim-land/task_close_enter.xml b/core/res/res/anim-land/task_close_enter.xml deleted file mode 100644 index facc42b..0000000 --- a/core/res/res/anim-land/task_close_enter.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top"> - - <alpha android:fromAlpha="0" android:toAlpha="1.0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="300" - android:duration="400"/> - - <translate android:fromXDelta="-120%" android:toXDelta="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="300" - android:duration="400" /> - - <scale android:fromXScale=".5" android:toXScale="1.0" - android:fromYScale=".5" android:toYScale="1.0" - android:pivotY="50%p" android:pivotX="0%p" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="300" - android:duration="400" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-land/task_close_exit.xml b/core/res/res/anim-land/task_close_exit.xml deleted file mode 100644 index e104c33..0000000 --- a/core/res/res/anim-land/task_close_exit.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal"> - - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_quad" - android:duration="300"/> - - <translate android:fromXDelta="0" android:toXDelta="120%" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_cubic" - android:duration="300"/> - - <scale android:fromXScale="1.0" android:toXScale="0.5" - android:fromYScale="1.0" android:toYScale="0.5" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:pivotY="50%p" android:pivotX="100%p" - android:interpolator="@interpolator/accelerate_quad" - android:duration="300" /> - - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:duration="700" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-land/task_open_enter.xml b/core/res/res/anim-land/task_open_enter.xml deleted file mode 100644 index dc7c7a9..0000000 --- a/core/res/res/anim-land/task_open_enter.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top"> - - <alpha android:fromAlpha="0" android:toAlpha="1.0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="300" - android:duration="400"/> - - <translate android:fromXDelta="120%" android:toXDelta="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="300" - android:duration="400" /> - - <scale android:fromXScale=".5" android:toXScale="1.0" - android:fromYScale=".5" android:toYScale="1.0" - android:pivotY="50%p" android:pivotX="100%p" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="300" - android:duration="400" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-land/task_open_exit.xml b/core/res/res/anim-land/task_open_exit.xml deleted file mode 100644 index 701afa6..0000000 --- a/core/res/res/anim-land/task_open_exit.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal"> - - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_quad" - android:duration="300"/> - - <translate android:fromXDelta="0" android:toXDelta="-120%" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_cubic" - android:duration="300"/> - - <scale android:fromXScale="1.0" android:toXScale="0.5" - android:fromYScale="1.0" android:toYScale="0.5" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:pivotY="50%p" android:pivotX="0%p" - android:interpolator="@interpolator/accelerate_quad" - android:duration="300" /> - - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:duration="700" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-sw720dp/task_close_enter.xml b/core/res/res/anim-sw720dp/task_close_enter.xml deleted file mode 100644 index e25978b..0000000 --- a/core/res/res/anim-sw720dp/task_close_enter.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top"> - - <alpha android:fromAlpha="0" android:toAlpha="1.0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="500" - android:duration="400"/> - - <translate android:fromYDelta="-50%" android:toYDelta="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="500" - android:duration="400" /> - - <scale android:fromXScale=".8" android:toXScale="1.0" - android:fromYScale=".8" android:toYScale="1.0" - android:pivotX="50%p" android:pivotY="0%p" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="500" - android:duration="400" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-sw720dp/task_close_exit.xml b/core/res/res/anim-sw720dp/task_close_exit.xml deleted file mode 100644 index 2d7e2a6..0000000 --- a/core/res/res/anim-sw720dp/task_close_exit.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal"> - - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_quad" - android:duration="350"/> - - <translate android:fromYDelta="0" android:toYDelta="50%" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_cubic" - android:duration="350"/> - - <scale android:fromXScale="1.0" android:toXScale="0.8" - android:fromYScale="1.0" android:toYScale="0.8" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:pivotX="50%p" android:pivotY="100%p" - android:interpolator="@interpolator/accelerate_quad" - android:duration="350" /> - - <!-- This is needed to keep the animation running while task_close_enter completes --> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:duration="900" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-sw720dp/task_open_enter.xml b/core/res/res/anim-sw720dp/task_open_enter.xml deleted file mode 100644 index d583b6e..0000000 --- a/core/res/res/anim-sw720dp/task_open_enter.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top"> - - <alpha android:fromAlpha="0" android:toAlpha="1.0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="500" - android:duration="400"/> - - <translate android:fromYDelta="50%" android:toYDelta="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quint" - android:startOffset="500" - android:duration="400" /> - - <scale android:fromXScale=".8" android:toXScale="1.0" - android:fromYScale=".8" android:toYScale="1.0" - android:pivotX="50%p" android:pivotY="100%p" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/decelerate_quad" - android:startOffset="500" - android:duration="400" /> -</set>
\ No newline at end of file diff --git a/core/res/res/anim-sw720dp/task_open_exit.xml b/core/res/res/anim-sw720dp/task_open_exit.xml deleted file mode 100644 index dd25a0c..0000000 --- a/core/res/res/anim-sw720dp/task_open_exit.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2009, 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. -*/ ---> - -<set xmlns:android="http://schemas.android.com/apk/res/android" - android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal"> - - <alpha android:fromAlpha="1.0" android:toAlpha="0" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_quad" - android:duration="350"/> - - <translate android:fromYDelta="0" android:toYDelta="-50%" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@interpolator/accelerate_cubic" - android:duration="350"/> - - <scale android:fromXScale="1.0" android:toXScale="0.8" - android:fromYScale="1.0" android:toYScale="0.8" - android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:pivotX="50%p" android:pivotY="0%p" - android:interpolator="@interpolator/accelerate_quad" - android:duration="350" /> - - <!-- This is needed to keep the animation running while task_open_enter completes --> - <alpha android:fromAlpha="1.0" android:toAlpha="1.0" - android:duration="900" /> -</set>
\ No newline at end of file diff --git a/core/res/res/drawable-hdpi/dialog_background_mtrl_mult.9.png b/core/res/res/drawable-hdpi/dialog_background_mtrl_mult.9.png Binary files differnew file mode 100644 index 0000000..0c3c3b4 --- /dev/null +++ b/core/res/res/drawable-hdpi/dialog_background_mtrl_mult.9.png diff --git a/core/res/res/drawable-hdpi/ic_afw_icon.png b/core/res/res/drawable-hdpi/ic_afw_icon.png Binary files differnew file mode 100644 index 0000000..fc986fe --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_afw_icon.png diff --git a/core/res/res/drawable-mdpi/dialog_background_mtrl_mult.9.png b/core/res/res/drawable-mdpi/dialog_background_mtrl_mult.9.png Binary files differnew file mode 100644 index 0000000..8322ae3 --- /dev/null +++ b/core/res/res/drawable-mdpi/dialog_background_mtrl_mult.9.png diff --git a/core/res/res/drawable-mdpi/ic_afw_icon.png b/core/res/res/drawable-mdpi/ic_afw_icon.png Binary files differnew file mode 100644 index 0000000..deb7966 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_afw_icon.png diff --git a/core/res/res/drawable-xhdpi/dialog_background_mtrl_mult.9.png b/core/res/res/drawable-xhdpi/dialog_background_mtrl_mult.9.png Binary files differnew file mode 100644 index 0000000..e6c0047 --- /dev/null +++ b/core/res/res/drawable-xhdpi/dialog_background_mtrl_mult.9.png diff --git a/core/res/res/drawable-xhdpi/ic_afw_icon.png b/core/res/res/drawable-xhdpi/ic_afw_icon.png Binary files differnew file mode 100644 index 0000000..ec4f44b --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_afw_icon.png diff --git a/core/res/res/drawable-xxhdpi/dialog_background_mtrl_mult.9.png b/core/res/res/drawable-xxhdpi/dialog_background_mtrl_mult.9.png Binary files differnew file mode 100644 index 0000000..bb9debb --- /dev/null +++ b/core/res/res/drawable-xxhdpi/dialog_background_mtrl_mult.9.png diff --git a/core/res/res/drawable-xxhdpi/ic_afw_icon.png b/core/res/res/drawable-xxhdpi/ic_afw_icon.png Binary files differnew file mode 100644 index 0000000..6888377 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/ic_afw_icon.png diff --git a/core/res/res/drawable/btn_borderless_material.xml b/core/res/res/drawable/btn_borderless_material.xml index 1967b38..a459089 100644 --- a/core/res/res/drawable/btn_borderless_material.xml +++ b/core/res/res/drawable/btn_borderless_material.xml @@ -15,7 +15,7 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="?attr/colorControlHighlight"> + android:color="?attr/colorControlHighlight"> <item android:id="@id/mask" android:drawable="@drawable/btn_mtrl_alpha" /> </ripple> diff --git a/core/res/res/drawable/btn_default_material.xml b/core/res/res/drawable/btn_default_material.xml index acec900..9cee3ab 100644 --- a/core/res/res/drawable/btn_default_material.xml +++ b/core/res/res/drawable/btn_default_material.xml @@ -15,7 +15,7 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="?attr/colorControlHighlight"> + android:color="?attr/colorControlHighlight"> <item> <nine-patch android:src="@drawable/btn_mtrl_alpha" android:tint="?attr/colorButtonNormal" /> diff --git a/core/res/res/drawable/btn_toggle_material.xml b/core/res/res/drawable/btn_toggle_material.xml index 4532c77..73fe4d3 100644 --- a/core/res/res/drawable/btn_toggle_material.xml +++ b/core/res/res/drawable/btn_toggle_material.xml @@ -21,7 +21,7 @@ android:insetRight="4dp"> <layer-list android:paddingMode="stack"> <item> - <ripple android:tint="?attr/colorControlHighlight"> + <ripple android:color="?attr/colorControlHighlight"> <item> <nine-patch android:src="@drawable/btn_toggle_mtrl_alpha" android:tint="?attr/colorButtonNormal" /> diff --git a/packages/SystemUI/res/drawable/status_bar_item_background.xml b/core/res/res/drawable/dialog_background_shadow_material.xml index 3a50aa9..0554920 100644 --- a/packages/SystemUI/res/drawable/status_bar_item_background.xml +++ b/core/res/res/drawable/dialog_background_shadow_material.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2006 The Android Open Source Project +<!-- Copyright (C) 2014 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. @@ -14,12 +14,7 @@ limitations under the License. --> -<layer-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:opacity="translucent" - > - <item - android:drawable="@drawable/notification_item_background_color" - /> -</layer-list> - +<nine-patch xmlns:android="http://schemas.android.com/apk/res/android" + android:src="@drawable/dialog_background_mtrl_mult" + android:tint="?attr/colorBackground" + android:tintMode="multiply" /> diff --git a/core/res/res/drawable/edit_text_material.xml b/core/res/res/drawable/edit_text_material.xml index 26fd889..86cec8f 100644 --- a/core/res/res/drawable/edit_text_material.xml +++ b/core/res/res/drawable/edit_text_material.xml @@ -15,8 +15,7 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="?attr/colorControlActivated" - android:tintMode="src_over"> + android:color="?attr/colorControlActivated"> <item> <selector> <item android:state_enabled="false"> diff --git a/core/res/res/drawable/item_background_borderless_material.xml b/core/res/res/drawable/item_background_borderless_material.xml index c2a1c127..b730618 100644 --- a/core/res/res/drawable/item_background_borderless_material.xml +++ b/core/res/res/drawable/item_background_borderless_material.xml @@ -15,5 +15,4 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="?attr/colorControlHighlight" - android:pinned="true" /> + android:color="?attr/colorControlHighlight" /> diff --git a/core/res/res/drawable/item_background_material.xml b/core/res/res/drawable/item_background_material.xml index 039ca51..45ff181 100644 --- a/core/res/res/drawable/item_background_material.xml +++ b/core/res/res/drawable/item_background_material.xml @@ -15,8 +15,8 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="?attr/colorControlHighlight"> + android:color="?attr/colorControlHighlight"> <item android:id="@id/mask"> <color android:color="@color/white" /> </item> -</ripple>
\ No newline at end of file +</ripple> diff --git a/core/res/res/drawable/notification_bg_dim.xml b/core/res/res/drawable/notification_bg_dim.xml index ae03d82..5c245f8 100644 --- a/core/res/res/drawable/notification_bg_dim.xml +++ b/core/res/res/drawable/notification_bg_dim.xml @@ -15,9 +15,7 @@ ~ limitations under the License --> -<ripple - xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="#ff444444" - > +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="#ff444444"> <item android:drawable="@drawable/notification_bg_normal" /> </ripple>
\ No newline at end of file diff --git a/core/res/res/drawable/notification_material_bg_dim.xml b/core/res/res/drawable/notification_material_bg_dim.xml index a908be7..9b691e6 100644 --- a/core/res/res/drawable/notification_material_bg_dim.xml +++ b/core/res/res/drawable/notification_material_bg_dim.xml @@ -15,15 +15,18 @@ ~ limitations under the License --> -<ripple - xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="#ffffffff" - android:tintMode="src_over" - > +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/white"> <item> <shape> <solid android:color="#d4ffffff" /> <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> </shape> </item> -</ripple>
\ No newline at end of file + <item android:id="@id/mask"> + <shape> + <solid android:color="@color/white" /> + <corners android:radius="@dimen/notification_material_rounded_rect_radius" /> + </shape> + </item> +</ripple> diff --git a/core/res/res/layout/dialog_custom_title_material.xml b/core/res/res/layout/dialog_custom_title_material.xml index 1bb93eb..550b72e 100644 --- a/core/res/res/layout/dialog_custom_title_material.xml +++ b/core/res/res/layout/dialog_custom_title_material.xml @@ -23,6 +23,7 @@ This is a custom layout for a dialog. android:fitsSystemWindows="true"> <FrameLayout android:id="@android:id/title_container" android:layout_width="match_parent" + android:layout_height="?android:attr/windowTitleSize" android:layout_weight="0" android:gravity="center_vertical|start" style="?android:attr/windowTitleBackgroundStyle" /> diff --git a/core/res/res/layout/dialog_title_material.xml b/core/res/res/layout/dialog_title_material.xml index b92c1e7..918c8f1 100644 --- a/core/res/res/layout/dialog_title_material.xml +++ b/core/res/res/layout/dialog_title_material.xml @@ -22,7 +22,7 @@ enabled. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:fitsSystemWindows="true"> - <TextView android:id="@+id/alertTitle" + <TextView android:id="@+id/title" style="?android:attr/windowTitleStyle" android:singleLine="true" android:ellipsize="end" diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml index bdf27c8..0564a8f 100644 --- a/core/res/res/layout/notification_template_material_big_base.xml +++ b/core/res/res/layout/notification_template_material_big_base.xml @@ -145,7 +145,7 @@ android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:visibility="gone" - style="@style/Widget.Material.Light.ProgressBar.Horizontal" + style="@style/Widget.StatusBar.Material.ProgressBar" /> </LinearLayout> <ImageView diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml index c89b9f9..f8e1986 100644 --- a/core/res/res/layout/notification_template_material_big_media.xml +++ b/core/res/res/layout/notification_template_material_big_media.xml @@ -38,7 +38,6 @@ android:minHeight="@dimen/notification_large_icon_height" android:paddingTop="2dp" android:orientation="vertical" - android:background="@color/notification_media_info_bg" > <LinearLayout android:id="@+id/line1" @@ -147,7 +146,7 @@ android:layout_height="6dp" android:layout_gravity="top" android:visibility="gone" - style="@style/Widget.StatusBar.Material.ProgressBar" + style="@style/Widget.StatusBar.Material.ProgressBar.Media" /> </FrameLayout> </LinearLayout> diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml index 6f8c3a9..1de5add 100644 --- a/core/res/res/layout/notification_template_material_big_text.xml +++ b/core/res/res/layout/notification_template_material_big_text.xml @@ -99,7 +99,7 @@ android:layout_marginEnd="8dp" android:visibility="gone" android:layout_weight="0" - style="@style/Widget.Material.Light.ProgressBar.Horizontal" + style="@style/Widget.StatusBar.Material.ProgressBar" /> <TextView android:id="@+id/big_text" android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent" diff --git a/core/res/res/layout/preference_material.xml b/core/res/res/layout/preference_material.xml index a4fe73d..a959913 100644 --- a/core/res/res/layout/preference_material.xml +++ b/core/res/res/layout/preference_material.xml @@ -31,7 +31,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:minWidth="58dip" - android:gravity="left|center_vertical" + android:gravity="start|center_vertical" android:orientation="horizontal"> <ImageView android:id="@+android:id/icon" diff --git a/core/res/res/layout/preference_widget_checkbox.xml b/core/res/res/layout/preference_widget_checkbox.xml index bfd7f0a..f794346 100644 --- a/core/res/res/layout/preference_widget_checkbox.xml +++ b/core/res/res/layout/preference_widget_checkbox.xml @@ -21,4 +21,5 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:focusable="false" - android:clickable="false" /> + android:clickable="false" + android:background="@null" /> diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml index 534c7ec..04a310b 100644 --- a/core/res/res/layout/preference_widget_switch.xml +++ b/core/res/res/layout/preference_widget_switch.xml @@ -21,4 +21,5 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="16dip" - android:focusable="false" /> + android:focusable="false" + android:background="@null" /> diff --git a/core/res/res/values-large/themes.xml b/core/res/res/values-large/themes.xml deleted file mode 100644 index 8c8f86c..0000000 --- a/core/res/res/values-large/themes.xml +++ /dev/null @@ -1,64 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2010, 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. -*/ ---> - -<!-- -=============================================================== - PLEASE READ -=============================================================== - -The Holo themes must not be modified in order to pass CTS. -Many related themes and styles depend on other values defined in this file. -If you would like to provide custom themes and styles for your device, -please see themes_device_defaults.xml. - -=============================================================== - PLEASE READ -=============================================================== - --> -<resources> - <style name="Theme.Holo.DialogWhenLarge" - parent="@android:style/Theme.Holo.Dialog.FixedSize"> - <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> - </style> - <style name="Theme.Holo.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.Holo.Dialog.NoActionBar.FixedSize"> - <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> - </style> - <style name="Theme.Holo.Light.DialogWhenLarge" - parent="@android:style/Theme.Holo.Light.Dialog.FixedSize"> - </style> - <style name="Theme.Holo.Light.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.Holo.Light.Dialog.NoActionBar.FixedSize"> - </style> - - <style name="Theme.Material.DialogWhenLarge" - parent="@android:style/Theme.Material.Dialog.FixedSize"> - <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> - </style> - <style name="Theme.Material.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.Material.Dialog.NoActionBar.FixedSize"> - <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> - </style> - <style name="Theme.Material.Light.DialogWhenLarge" - parent="@android:style/Theme.Material.Light.Dialog.FixedSize"> - </style> - <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.Material.Light.Dialog.NoActionBar.FixedSize"> - </style> -</resources> diff --git a/core/res/res/values-large/themes_device_defaults.xml b/core/res/res/values-large/themes_device_defaults.xml index d57e827..d252c27 100644 --- a/core/res/res/values-large/themes_device_defaults.xml +++ b/core/res/res/values-large/themes_device_defaults.xml @@ -31,18 +31,12 @@ easier. =============================================================== --> <resources> - <style name="Theme.DeviceDefault.DialogWhenLarge" - parent="@android:style/Theme.DeviceDefault.Dialog.FixedSize"> + <style name="Theme.DeviceDefault.DialogWhenLarge" parent="@style/Theme.DeviceDefault.Dialog.FixedSize"> <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> </style> - <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar.FixedSize"> + <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.Dialog.NoActionBar.FixedSize"> <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> </style> - <style name="Theme.DeviceDefault.Light.DialogWhenLarge" - parent="@android:style/Theme.DeviceDefault.Light.Dialog.FixedSize"> - </style> - <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize"> - </style> + <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="@style/Theme.DeviceDefault.Light.Dialog.FixedSize" /> + <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize" /> </resources> diff --git a/core/res/res/values-large/themes_holo.xml b/core/res/res/values-large/themes_holo.xml new file mode 100644 index 0000000..3f03932 --- /dev/null +++ b/core/res/res/values-large/themes_holo.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, 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. +*/ +--> + +<!-- +=============================================================== + PLEASE READ +=============================================================== + +The Holo themes must not be modified in order to pass CTS. +Many related themes and styles depend on other values defined in this file. +If you would like to provide custom themes and styles for your device, +please see themes_device_defaults.xml. + +=============================================================== + PLEASE READ +=============================================================== + --> +<resources> + <style name="Theme.Holo.DialogWhenLarge" parent="@style/Theme.Holo.Dialog.FixedSize"> + <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> + </style> + <style name="Theme.Holo.DialogWhenLarge.NoActionBar" parent="@style/Theme.Holo.Dialog.NoActionBar.FixedSize"> + <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> + </style> + <style name="Theme.Holo.Light.DialogWhenLarge" parent="@style/Theme.Holo.Light.Dialog.FixedSize" /> + <style name="Theme.Holo.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Holo.Light.Dialog.NoActionBar.FixedSize" /> +</resources> diff --git a/core/res/res/values-large/themes_material.xml b/core/res/res/values-large/themes_material.xml new file mode 100644 index 0000000..2781608 --- /dev/null +++ b/core/res/res/values-large/themes_material.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> + +<!-- +=============================================================== + PLEASE READ +=============================================================== + +The Material themes must not be modified in order to pass CTS. +Many related themes and styles depend on other values defined in this file. +If you would like to provide custom themes and styles for your device, +please see themes_device_defaults.xml. + +=============================================================== + PLEASE READ +=============================================================== + --> +<resources> + <style name="Theme.Material.DialogWhenLarge" parent="@style/Theme.Material.Dialog.FixedSize"> + <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> + </style> + <style name="Theme.Material.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Dialog.NoActionBar.FixedSize"> + <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> + </style> + <style name="Theme.Material.Light.DialogWhenLarge" parent="@style/Theme.Material.Light.Dialog.FixedSize" /> + <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Light.Dialog.NoActionBar.FixedSize" /> +</resources> diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml index 8d82a17..6052fb0 100644 --- a/core/res/res/values-watch/config.xml +++ b/core/res/res/values-watch/config.xml @@ -36,4 +36,8 @@ <!-- Maximum velocity to initiate a fling, as measured in dips per second. --> <dimen name="config_viewMaxFlingVelocity">8000dp</dimen> + <!-- Number of notifications to keep in the notification service historical archive. + Reduced intentionally for watches to retain minimal memory footprint --> + <integer name="config_notificationServiceArchiveSize">1</integer> + </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a751906..5213896 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -496,6 +496,12 @@ <!-- Internal layout used internally for window decor --> <attr name="windowActionBarFullscreenDecorLayout" format="reference" /> + <!-- The duration, in milliseconds, of the window background fade duration + when transitioning into or away from an Activity when called with an + Activity Transition. Corresponds to + {@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. --> + <attr name="windowTransitionBackgroundFadeDuration" format="integer"/> + <!-- ============ --> <!-- Alert Dialog styles --> <!-- ============ --> @@ -1813,6 +1819,11 @@ Corresponds to {@link android.view.Window#setNavigationBarColor(int)}. --> <attr name="navigationBarColor" format="color" /> + <!-- The duration, in milliseconds, of the window background fade duration + when transitioning into or away from an Activity when called with an + Activity Transition. Corresponds to + {@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. --> + <attr name="windowTransitionBackgroundFadeDuration" /> </declare-styleable> <!-- The set of attributes that describe a AlertDialog's theme. --> @@ -3859,8 +3870,11 @@ <attr name="inputType" /> </declare-styleable> <declare-styleable name="PopupWindow"> + <!-- The background to use for the popup window. --> <attr name="popupBackground" format="reference|color" /> + <!-- The animation style to use for the popup window. --> <attr name="popupAnimationStyle" format="reference" /> + <!-- Whether the popup window should overlap its anchor view. --> <attr name="overlapAnchor" format="boolean" /> </declare-styleable> <declare-styleable name="ViewAnimator"> @@ -4675,12 +4689,8 @@ <!-- Drawable used to show animated touch feedback. --> <declare-styleable name="RippleDrawable"> - <!-- The tint to use for ripple effects. This attribute is required. --> - <attr name="tint" /> - <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default value is src_atop, which draws over the opaque parts of the drawable. --> - <attr name="tintMode" /> - <!-- Whether to pin ripple effects to the center of the drawable. Default value is false. --> - <attr name="pinned" format="boolean" /> + <!-- The color to use for ripple effects. This attribute is required. --> + <attr name="color" /> </declare-styleable> <declare-styleable name="ScaleDrawable"> @@ -4776,7 +4786,7 @@ </declare-styleable> <!-- ========================== --> - <!-- Vector drawable class --> + <!-- VectorDrawable class --> <!-- ========================== --> <eat-comment /> @@ -4796,7 +4806,7 @@ <attr name="height" /> </declare-styleable> - <!-- Defines the group used in Vector Drawables. --> + <!-- Defines the group used in VectorDrawables. --> <declare-styleable name="VectorDrawableGroup"> <!-- The Name of this group --> <attr name="name" /> @@ -4818,7 +4828,7 @@ <attr name="alpha" /> </declare-styleable> - <!-- Defines the path used in Vector Drawables. --> + <!-- Defines the path used in VectorDrawables. --> <declare-styleable name="VectorDrawablePath"> <!-- The Name of this path --> <attr name="name" /> @@ -4859,6 +4869,25 @@ </declare-styleable> <!-- ========================== --> + <!-- AnimatedVectorDrawable class --> + <!-- ========================== --> + <eat-comment /> + + <!-- Define the AnimatedVectorDrawable. --> + <declare-styleable name="AnimatedVectorDrawable"> + <!-- The static vector drawable. --> + <attr name="drawable" /> + </declare-styleable> + + <!-- Defines the target path or group used in the AnimatedVectorDrawable. --> + <declare-styleable name="AnimatedVectorDrawableTarget"> + <!-- The name of this target path or group --> + <attr name="name" /> + <!-- The animation for this target path or group --> + <attr name="animation" /> + </declare-styleable> + + <!-- ========================== --> <!-- Animation class attributes --> <!-- ========================== --> <eat-comment /> @@ -6394,6 +6423,16 @@ <attr name="settingsActivity" /> </declare-styleable> + <!-- Use <code>voice-enrollment-application</code> + as the root tag of the XML resource that escribes the supported keyphrases (hotwords) + by the enrollment application. + Described here are the attributes that can be included in that tag. --> + <declare-styleable name="VoiceEnrollmentApplication"> + <attr name="searchKeyphraseId" format="integer" /> + <attr name="searchKeyphrase" format="string" /> + <attr name="searchKeyphraseSupportedLocales" format="string" /> + </declare-styleable> + <!-- Attributes used to style the Action Bar. --> <declare-styleable name="ActionBar"> <!-- The type of navigation to use. --> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 9bf2ce8..9f6c7ad 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -135,7 +135,6 @@ <color name="notification_action_legacy_color_filter">#ff555555</color> <color name="notification_media_action_bg">#00000000</color> - <color name="notification_media_info_bg">#40FFFFFF</color> <color name="notification_media_progress">#FFFFFFFF</color> <!-- Keyguard colors --> diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index fdbe0a0..faa9fb7 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -89,6 +89,9 @@ <color name="material_teal_A200">#ff18ffff</color> <color name="material_teal_A400">#ff00e5ff</color> + <!-- Accent color used by Settings --> + <color name="material_dark_teal_A400">#ff009688</color> + <color name="material_green_100">#ffb7e1cd</color> <color name="material_green_300">#ff57bb8a</color> <color name="material_green_500">#ff0f9d58</color> @@ -143,7 +146,10 @@ <color name="material_blue_grey_600">#ff546e7a</color> <color name="material_blue_grey_700">#ff455a64</color> <color name="material_blue_grey_800">#ff37474f</color> + <!-- Primary color used by Settings --> <color name="material_blue_grey_900">#ff263238</color> + <!-- Primary dark color used by Settings --> + <color name="material_blue_grey_950">#ff21272b</color> <color name="material_brown_100">#ffd7ccc8</color> <color name="material_brown_300">#ffa1887f</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3b36ee6..830c64c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -589,7 +589,7 @@ <!-- Display low battery warning when battery level dips to this value. Also, the battery stats are flushed to disk when we hit this level. --> - <integer name="config_criticalBatteryWarningLevel">4</integer> + <integer name="config_criticalBatteryWarningLevel">5</integer> <!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. --> <integer name="config_shutdownBatteryTemperature">680</integer> @@ -597,8 +597,9 @@ <!-- Display low battery warning when battery level dips to this value --> <integer name="config_lowBatteryWarningLevel">15</integer> - <!-- Close low battery warning when battery level reaches this value --> - <integer name="config_lowBatteryCloseWarningLevel">20</integer> + <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel + plus this --> + <integer name="config_lowBatteryCloseWarningBump">5</integer> <!-- Default color for notification LED. --> <color name="config_defaultNotificationColor">#ffffffff</color> @@ -627,6 +628,9 @@ <!-- Default value for LED off time when the battery is low on charge in miliseconds --> <integer name="config_notificationsBatteryLedOff">2875</integer> + <!-- Number of notifications to keep in the notification service historical archive --> + <integer name="config_notificationServiceArchiveSize">250</integer> + <!-- Allow the menu hard key to be disabled in LockScreen on some devices --> <bool name="config_disableMenuKeyInLockScreen">false</bool> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 16bba5b..202c127 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2132,7 +2132,6 @@ <public type="attr" name="strokeLineJoin" /> <public type="attr" name="clipToPath" /> <public type="attr" name="requiredForProfile"/> - <public type="attr" name="pinned" /> <public type="attr" name="colorControlNormal" /> <public type="attr" name="colorControlActivated" /> <public type="attr" name="colorButtonNormal" /> @@ -2186,6 +2185,11 @@ <public type="attr" name="translateY" /> <public type="attr" name="selectableItemBackgroundBorderless" /> <public type="attr" name="elegantTextHeight" /> + <public type="attr" name="searchKeyphraseId" /> + <public type="attr" name="searchKeyphrase" /> + <public type="attr" name="searchKeyphraseSupportedLocales" /> + <public type="attr" name="windowTransitionBackgroundFadeDuration" /> + <public type="attr" name="overlapAnchor" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> @@ -2197,6 +2201,8 @@ <public type="style" name="Widget.FastScroll" /> <public type="style" name="Widget.StackView" /> + <public type="style" name="Widget.Toolbar" /> + <public type="style" name="Widget.Toolbar.Button.Navigation" /> <public type="style" name="Widget.Holo.FastScroll" /> <public type="style" name="Widget.Holo.StackView" /> @@ -2247,6 +2253,8 @@ <public type="style" name="TextAppearance.Material.Widget.TextView.PopupMenu" /> <public type="style" name="TextAppearance.Material.Widget.TextView.SpinnerItem" /> + <public type="style" name="Theme.DeviceDefault.Settings" /> + <public type="style" name="Theme.Material" /> <public type="style" name="Theme.Material.Dialog" /> <public type="style" name="Theme.Material.Dialog.MinWidth" /> @@ -2260,6 +2268,7 @@ <public type="style" name="Theme.Material.NoActionBar.Overscan" /> <public type="style" name="Theme.Material.NoActionBar.TranslucentDecor" /> <public type="style" name="Theme.Material.Panel" /> + <public type="style" name="Theme.Material.Settings" /> <public type="style" name="Theme.Material.Voice" /> <public type="style" name="Theme.Material.Wallpaper" /> <public type="style" name="Theme.Material.Wallpaper.NoTitleBar" /> @@ -2343,6 +2352,8 @@ <public type="style" name="Widget.Material.TabWidget" /> <public type="style" name="Widget.Material.TextView" /> <public type="style" name="Widget.Material.TextView.SpinnerItem" /> + <public type="style" name="Widget.Material.Toolbar" /> + <public type="style" name="Widget.Material.Toolbar.Button.Navigation" /> <public type="style" name="Widget.Material.WebTextView" /> <public type="style" name="Widget.Material.WebView" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 23a117f..0ddfeb6 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -460,7 +460,7 @@ <string name="user_owner_label">Personal apps</string> <!-- Label for a corporate profile in the intent forwarding app. --> - <string name="managed_profile_label">Android for Work</string> + <string name="managed_profile_label">Android Work</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgrouplab_costMoney">Services that cost you money</string> @@ -1122,6 +1122,12 @@ interface of a voice interaction service. Should never be needed for normal apps.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_manageVoiceKeyphrases">manage voice keyphrases</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_manageVoiceKeyphrases">Allows the holder to manage the keyphrases for voice hotword detection. + Should never be needed for normal apps.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_bindRemoteDisplay">bind to a remote display</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_bindRemoteDisplay">Allows the holder to bind to the top-level @@ -1261,10 +1267,10 @@ permission that an application must be granted by the user. Instead, it is part of a mechanism that applications use to indicate to the system that they want to do scheduled background work. --> - <string name="permlab_bindTaskService">run the application\'s scheduled background work</string> + <string name="permlab_bindJobService">run the application\'s scheduled background work</string> <!-- Description of an application permission, so that the user can understand what is being done if they are curious. --> - <string name="permdesc_bindTaskService">This permission allows the Android system to run the application in the background when requested.</string> + <string name="permdesc_bindJobService">This permission allows the Android system to run the application in the background when requested.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_diagnostic">read/write to resources owned by diag</string> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 3d73995..ddd82c3 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -344,7 +344,9 @@ please see styles_device_defaults.xml. <item name="android:textColor">#66000000</item> </style> - <style name="Widget.StatusBar.Material.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal"> + <style name="Widget.StatusBar.Material.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" /> + + <style name="Widget.StatusBar.Material.ProgressBar.Media"> <item name="android:progressDrawable">@drawable/notification_material_media_progress</item> </style> @@ -622,6 +624,7 @@ please see styles_device_defaults.xml. <style name="Widget.Material.Spinner.DropDown.ActionBar"> <item name="background">@drawable/spinner_background_material</item> + <item name="overlapAnchor">true</item> </style> <style name="Widget.Material.TabWidget" parent="Widget.TabWidget"> @@ -646,6 +649,14 @@ please see styles_device_defaults.xml. <item name="maxWidth">180dip</item> </style> + <style name="Widget.Material.Toolbar" parent="Widget.Toolbar"> + <item name="android:navigationButtonStyle">@android:style/Widget.Material.Toolbar.Button.Navigation</item> + </style> + + <style name="Widget.Material.Toolbar.Button.Navigation" parent="Widget.Toolbar.Button.Navigation"> + <item name="android:background">?attr/selectableItemBackgroundBorderless</item> + </style> + <style name="Widget.Material.WebTextView" parent="Widget.WebTextView"/> <style name="Widget.Material.WebView" parent="Widget.WebView"/> @@ -952,12 +963,17 @@ please see styles_device_defaults.xml. <!-- Window title --> <style name="WindowTitleBackground.Material"> <item name="background">@null</item> + <item name="paddingStart">16dp</item> + <item name="paddingEnd">16dp</item> + <item name="paddingTop">16dp</item> </style> <style name="WindowTitle.Material"> <item name="singleLine">true</item> <item name="textAppearance">@style/TextAppearance.Material.WindowTitle</item> <item name="shadowRadius">0</item> + <item name="ellipsize">end</item> + <item name="textAlignment">viewStart</item> </style> <style name="DialogWindowTitle.Material"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index de323d7..8fad07a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1510,7 +1510,7 @@ <java-symbol type="integer" name="config_defaultNotificationLedOn" /> <java-symbol type="integer" name="config_deskDockKeepsScreenOn" /> <java-symbol type="integer" name="config_lightSensorWarmupTime" /> - <java-symbol type="integer" name="config_lowBatteryCloseWarningLevel" /> + <java-symbol type="integer" name="config_lowBatteryCloseWarningBump" /> <java-symbol type="integer" name="config_lowBatteryWarningLevel" /> <java-symbol type="integer" name="config_networkPolicyDefaultWarning" /> <java-symbol type="integer" name="config_networkTransitionTimeout" /> @@ -1519,6 +1519,7 @@ <java-symbol type="integer" name="config_notificationsBatteryLedOn" /> <java-symbol type="integer" name="config_notificationsBatteryLowARGB" /> <java-symbol type="integer" name="config_notificationsBatteryMediumARGB" /> + <java-symbol type="integer" name="config_notificationServiceArchiveSize" /> <java-symbol type="integer" name="config_radioScanningTimeout" /> <java-symbol type="integer" name="config_screenBrightnessSettingMinimum" /> <java-symbol type="integer" name="config_screenBrightnessSettingMaximum" /> @@ -1680,7 +1681,6 @@ <java-symbol type="drawable" name="notification_material_bg" /> <java-symbol type="drawable" name="notification_material_media_progress" /> <java-symbol type="color" name="notification_media_action_bg" /> - <java-symbol type="color" name="notification_media_info_bg" /> <java-symbol type="color" name="notification_media_progress" /> <java-symbol type="id" name="media_action_area" /> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 6b7d861..27c8754 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -555,4 +555,7 @@ easier. <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar" /> + <!-- DeviceDefault theme for a window that should look like the Settings app. --> + <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings" /> + </resources> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index c14d7fc..97ad7e4 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -154,7 +154,7 @@ please see themes_device_defaults.xml. <item name="windowContentOverlay">@null</item> <item name="windowShowWallpaper">false</item> <item name="windowTitleStyle">@style/WindowTitle.Material</item> - <item name="windowTitleSize">25dip</item> + <item name="windowTitleSize">@dimen/action_bar_default_height_material</item> <item name="windowTitleBackgroundStyle">@style/WindowTitleBackground.Material</item> <item name="windowContentTransitions">false</item> <item name="windowAnimationStyle">@style/Animation.Material.Activity</item> @@ -321,6 +321,8 @@ please see themes_device_defaults.xml. <item name="actionModeFindDrawable">@drawable/ic_menu_find_material</item> <item name="actionModeWebSearchDrawable">@drawable/ic_menu_search_material</item> + <item name="toolbarStyle">@style/Widget.Material.Toolbar</item> + <item name="dividerVertical">?attr/listDivider</item> <item name="dividerHorizontal">?attr/listDivider</item> <item name="buttonBarStyle">@style/Widget.Material.ButtonBar</item> @@ -501,7 +503,7 @@ please see themes_device_defaults.xml. <item name="windowContentOverlay">@drawable/ab_solid_shadow_material</item> <item name="windowShowWallpaper">false</item> <item name="windowTitleStyle">@style/WindowTitle.Material</item> - <item name="windowTitleSize">25dip</item> + <item name="windowTitleSize">@dimen/action_bar_default_height_material</item> <item name="windowTitleBackgroundStyle">@style/WindowTitleBackground.Material</item> <item name="windowAnimationStyle">@style/Animation.Material.Activity</item> <item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item> @@ -511,6 +513,9 @@ please see themes_device_defaults.xml. <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item> <item name="statusBarColor">?attr/colorPrimaryDark</item> <item name="navigationBarColor">@color/black</item> + <item name="windowEnterTransition">@transition/fade</item> + <item name="windowSharedElementEnterTransition">@transition/move</item> + <item name="windowSharedElementExitTransition">@transition/move</item> <!-- Dialog attributes --> <item name="dialogTheme">@style/Theme.Material.Light.Dialog</item> @@ -667,6 +672,8 @@ please see themes_device_defaults.xml. <item name="actionModeFindDrawable">@drawable/ic_menu_find_material</item> <item name="actionModeWebSearchDrawable">@drawable/ic_menu_search_material</item> + <item name="toolbarStyle">@style/Widget.Material.Toolbar</item> + <item name="dividerVertical">?attr/listDivider</item> <item name="dividerHorizontal">?attr/listDivider</item> <item name="buttonBarStyle">@style/Widget.Material.Light.ButtonBar</item> @@ -1002,7 +1009,7 @@ please see themes_device_defaults.xml. <style name="Theme.Material.Dialog"> <item name="windowFrame">@null</item> <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item> - <item name="windowBackground">@drawable/dialog_background_material</item> + <item name="windowBackground">@drawable/dialog_background_shadow_material</item> <item name="windowIsFloating">true</item> <item name="windowContentOverlay">@null</item> <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item> @@ -1098,18 +1105,15 @@ please see themes_device_defaults.xml. <!-- Theme for a window that will be displayed either full-screen on smaller screens (small, normal) or as a dialog on larger screens (large, xlarge). --> - <style name="Theme.Material.DialogWhenLarge" parent="@style/Theme.Material"> - </style> + <style name="Theme.Material.DialogWhenLarge" parent="@style/Theme.Material" /> <!-- Theme for a window without a title bar that will be displayed either full-screen on smaller screens (small, normal) or as a dialog on larger screens (large, xlarge). --> - <style name="Theme.Material.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.NoActionBar"> - </style> + <style name="Theme.Material.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.NoActionBar" /> <!-- Theme for a presentation window on a secondary display. --> - <style name="Theme.Material.Dialog.Presentation" parent="@style/Theme.Material.NoActionBar.Fullscreen"> - </style> + <style name="Theme.Material.Dialog.Presentation" parent="@style/Theme.Material.NoActionBar.Fullscreen" /> <!-- Light material dialog themes --> @@ -1121,7 +1125,7 @@ please see themes_device_defaults.xml. <style name="Theme.Material.Light.Dialog"> <item name="windowFrame">@null</item> <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item> - <item name="windowBackground">?attr/colorBackground</item> + <item name="windowBackground">@drawable/dialog_background_shadow_material</item> <item name="windowIsFloating">true</item> <item name="windowContentOverlay">@null</item> <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item> @@ -1185,15 +1189,12 @@ please see themes_device_defaults.xml. <!-- Theme for a window that will be displayed either full-screen on smaller screens (small, normal) or as a dialog on larger screens (large, xlarge). --> - <style name="Theme.Material.Light.DialogWhenLarge" parent="@style/Theme.Material.Light"> - </style> + <style name="Theme.Material.Light.DialogWhenLarge" parent="@style/Theme.Material.Light" /> <!-- Theme for a window without an action bar that will be displayed either full-screen on smaller screens (small, normal) or as a dialog on larger screens (large, xlarge). --> - <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" - parent="@style/Theme.Material.Light.NoActionBar"> - </style> + <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Light.NoActionBar" /> <!-- Material light theme for alert dialog windows, which is used by the {@link android.app.AlertDialog} class. This is basically a dialog @@ -1215,8 +1216,7 @@ please see themes_device_defaults.xml. </style> <!-- Theme for a presentation window on a secondary display. --> - <style name="Theme.Material.Light.Dialog.Presentation" parent="@style/Theme.Material.Light.NoActionBar.Fullscreen" > - </style> + <style name="Theme.Material.Light.Dialog.Presentation" parent="@style/Theme.Material.Light.NoActionBar.Fullscreen" /> <!-- Default material (dark) for windows that want to have the user's selected wallpaper appear behind them. --> @@ -1232,4 +1232,11 @@ please see themes_device_defaults.xml. <item name="windowNoTitle">true</item> </style> + <!-- Default theme for Settings and activities launched from Settings. --> + <style name="Theme.Material.Settings" parent="@style/Theme.Material.Light.DarkActionBar"> + <item name="colorPrimary">@color/material_blue_grey_900</item> + <item name="colorPrimaryDark">@color/material_blue_grey_950</item> + <item name="colorAccent">@color/material_dark_teal_A400</item> + </style> + </resources> diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java index 01283a6..c80d0bf 100644 --- a/core/tests/coretests/src/android/net/RouteInfoTest.java +++ b/core/tests/coretests/src/android/net/RouteInfoTest.java @@ -71,17 +71,19 @@ public class RouteInfoTest extends TestCase { } public void testMatches() { - class PatchedRouteInfo extends RouteInfo { + class PatchedRouteInfo { + private final RouteInfo mRouteInfo; + public PatchedRouteInfo(LinkAddress destination, InetAddress gateway, String iface) { - super(destination, gateway, iface); + mRouteInfo = new RouteInfo(destination, gateway, iface); } public boolean matches(InetAddress destination) { - return super.matches(destination); + return mRouteInfo.matches(destination); } } - RouteInfo r; + PatchedRouteInfo r; r = new PatchedRouteInfo(Prefix("2001:db8:f00::ace:d00d/127"), null, "rmnet0"); assertTrue(r.matches(Address("2001:db8:f00::ace:d00c"))); @@ -96,11 +98,11 @@ public class RouteInfoTest extends TestCase { assertFalse(r.matches(Address("192.0.0.21"))); assertFalse(r.matches(Address("8.8.8.8"))); - RouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0"); + PatchedRouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0"); assertTrue(ipv6Default.matches(Address("2001:db8::f00"))); assertFalse(ipv6Default.matches(Address("192.0.2.1"))); - RouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0"); + PatchedRouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0"); assertTrue(ipv4Default.matches(Address("255.255.255.255"))); assertTrue(ipv4Default.matches(Address("192.0.2.1"))); assertFalse(ipv4Default.matches(Address("2001:db8::f00"))); diff --git a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java index 7d72f3e..d850c7c 100644 --- a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java +++ b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java @@ -21,7 +21,7 @@ import android.graphics.RectF; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.view.inputmethod.CursorAnchorInfo; -import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder; +import android.view.inputmethod.CursorAnchorInfo.Builder; public class CursorAnchorInfoTest extends InstrumentationTestCase { // null represents a character that is invisible, for example because it's overlapped by some @@ -64,7 +64,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX); TRANSFORM_MATRIX.setScale(10.0f, 20.0f); - final CursorAnchorInfoBuilder builder = new CursorAnchorInfoBuilder(); + final Builder builder = new Builder(); builder.setSelectionRange(SELECTION_START, SELECTION_END) .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT) .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP, @@ -148,7 +148,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { final Matrix MATRIX3 = new Matrix(); MATRIX3.setTranslate(210.0f, 220.0f); final Matrix matrix = new Matrix(); - final CursorAnchorInfoBuilder builder = new CursorAnchorInfoBuilder(); + final Builder builder = new Builder(); matrix.set(MATRIX1); builder.setMatrix(matrix); @@ -171,7 +171,7 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { public void testBuilderAdd() throws Exception { // A negative index should be rejected. try { - new CursorAnchorInfoBuilder().addCharacterRect(-1, 0.0f, 0.0f, 0.0f, 0.0f); + new Builder().addCharacterRect(-1, 0.0f, 0.0f, 0.0f, 0.0f); } catch (IllegalArgumentException ex) { assertTrue(true); } @@ -191,4 +191,3 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { } } } - diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index c6bccfe..abb960c 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -106,6 +106,7 @@ font_src_files := \ ifeq ($(MINIMAL_FONT_FOOTPRINT),true) $(eval $(call create-font-symlink,Roboto-Black.ttf,Roboto-Bold.ttf)) +$(eval $(call create-font-symlink,Roboto-BlackItalic.ttf,Roboto-BoldItalic.ttf)) $(eval $(call create-font-symlink,Roboto-Light.ttf,Roboto-Regular.ttf)) $(eval $(call create-font-symlink,Roboto-LightItalic.ttf,Roboto-Italic.ttf)) $(eval $(call create-font-symlink,Roboto-Medium.ttf,Roboto-Regular.ttf)) @@ -120,6 +121,7 @@ $(eval $(call create-font-symlink,RobotoCondensed-BoldItalic.ttf,Roboto-BoldItal else # !MINIMAL_FONT font_src_files += \ Roboto-Black.ttf \ + Roboto-BlackItalic.ttf \ Roboto-Light.ttf \ Roboto-LightItalic.ttf \ Roboto-Medium.ttf \ diff --git a/data/fonts/Roboto-BlackItalic.ttf b/data/fonts/Roboto-BlackItalic.ttf Binary files differnew file mode 100644 index 0000000..3ebdc7d --- /dev/null +++ b/data/fonts/Roboto-BlackItalic.ttf diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index e5573bb..70fc6a2 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -25,6 +25,7 @@ PRODUCT_PACKAGES := \ Roboto-Italic.ttf \ Roboto-BoldItalic.ttf \ Roboto-Black.ttf \ + Roboto-BlackItalic.ttf \ Roboto-Light.ttf \ Roboto-LightItalic.ttf \ Roboto-Medium.ttf \ diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml index 646b33b..8c59fea 100644 --- a/data/fonts/system_fonts.xml +++ b/data/fonts/system_fonts.xml @@ -82,6 +82,7 @@ </nameset> <fileset> <file>Roboto-Black.ttf</file> + <file>Roboto-BlackItalic.ttf</file> </fileset> </family> diff --git a/docs/html/distribute/engage/analytics.jd b/docs/html/distribute/engage/analytics.jd new file mode 100644 index 0000000..5f7cade --- /dev/null +++ b/docs/html/distribute/engage/analytics.jd @@ -0,0 +1,50 @@ +page.title=Understand User Behavior +page.metaDescription=Use Google Analytics to learn what your users like and what keeps them coming back. +page.tags="analytics, user behavior" +page.image=/images/gp-analytics.jpg + +@jd:body + +<div class="figure"> + <img src="{@docRoot}images/gp-analytics.jpg" style="width:320px"> +</div> + + +<p> + Link your Google Play Developer Console with Google Analytics to learn much + more about how users interact with your app — before and after they + download it. +</p> + +<p> + Start by discovering how many people visit your Google Play listing page, + where they come from, and how many go on to install your app. Once the app is + launched, use Google Analytics to see which of your features are most + popular, where power users spend their time, who tends to make in-app + purchases, and more. +</p> + +<p> + Google Analytics delivers the numbers in real time, so you can act fast to + update your landing page and your app. <a href= + "http://www.google.com/analytics/mobile/">Learn more</a>. +</p> + +<p> + If you have a Google Analytics account already, linking it to Google Play + takes just a few moments. You can also link your Google Analytics account to + Admob to start gaining more user insights to improve your in-app marketing. +</p> + + <div class="headerLine clearfloat"> + <h2 id="related-resources"> + Related Resources + </h2> +</div> + +<div class="resource-widget resource-flow-layout col-13" + data-query="collection:distribute/engage/analytics" + data-sortorder="-timestamp" + data-cardsizes="9x3" + data-maxresults="6"> +</div>
\ No newline at end of file diff --git a/docs/html/distribute/engage/deep-linking.jd b/docs/html/distribute/engage/deep-linking.jd index 0417ba1..a25c3c6 100644 --- a/docs/html/distribute/engage/deep-linking.jd +++ b/docs/html/distribute/engage/deep-linking.jd @@ -1,13 +1,13 @@ page.title=Deep Link to Bring Users Back -page.metaDescription=Use deep links to bring your users into your apps from social posts or search. +page.metaDescription=Use deep links to bring your users into your apps from social posts, search, or ads. page.tags="app indexing, google+ signin" page.image=/images/gp-listing-4.jpg @jd:body <p> - Use deep links to bring your users into your apps from social posts or - search. + Use deep links to bring your users into your apps from social posts, + search, or ads. </p> <div class="headerLine"> @@ -47,6 +47,11 @@ page.image=/images/gp-listing-4.jpg <h2>Deep Linking from Google Search — App Indexing</h2> </div> + +<div style="float:right;"> + <img src="/images/gp-listing-4.jpg" style="padding-top:1em;padding-left:2em;"> +</div> + <p> Another way to bring users back to your apps is to apply for app indexing. </p> @@ -60,11 +65,29 @@ page.image=/images/gp-listing-4.jpg content</a>. </p> -<div> - <img src="{@docRoot}images/gp-listing-4.jpg" style="padding-top:1em;"> +<div class="clearfloat" style="margin-top:2em;"></div> + +<div style="float:right;width:340px;padding-left:2em;"> + <img src="/images/gp-ads-linking2.jpg" style="padding-top:1em;"> </div> -<div class="headerLine"> +<div class="headerLine "> +<h2>Deep Linking from Google Ads</h2> +</div> +<p> + Ads can remind users about the apps they already have. +</p> + +<p> + As with deep links from Google's organic search results, AdWords deep links + send users directly to the relevant pages in apps they already have on their + mobile device. A mobile search for "flights to London," for instance, could + take a user straight to the London page in a travel app. <a href= + "http://www.thinkwithgoogle.com/products/ads-apps.html" + class="external-link">Learn more</a>. +</p> + +<div class="headerLine clearfloat"> <h2 id="related-resources"> Related Resources </h2> diff --git a/docs/html/distribute/engage/engage_toc.cs b/docs/html/distribute/engage/engage_toc.cs index 0314f8c..596051a 100644 --- a/docs/html/distribute/engage/engage_toc.cs +++ b/docs/html/distribute/engage/engage_toc.cs @@ -37,6 +37,12 @@ </li> <li class="nav-section"> <div class="nav-section empty" style="font-weight:normal"><a href="<?cs + var:toroot?>distribute/engage/analytics.html"> + <span class="en">Understand User Behavior</span></a> + </div> + </li> + <li class="nav-section"> + <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/engage/app-updates.html"> <span class="en">Update Regularly</span></a> </div> diff --git a/docs/html/distribute/engage/index.jd b/docs/html/distribute/engage/index.jd index f8cd1ee..2b103c3 100644 --- a/docs/html/distribute/engage/index.jd +++ b/docs/html/distribute/engage/index.jd @@ -15,8 +15,8 @@ nonavpage=true <div class="resource-widget resource-flow-layout landing col-16" data-query="collection:distribute/engagelanding" - data-cardSizes="6x6" - data-maxResults="9"> + data-cardSizes="9x6,9x6,6x6,6x6,6x6,9x6,9x6,6x6,6x6,6x6" + data-maxResults="10"> </div> <h3>Related Resources</h3> diff --git a/docs/html/distribute/engage/widgets.jd b/docs/html/distribute/engage/widgets.jd index 286adea..6adb55c 100644 --- a/docs/html/distribute/engage/widgets.jd +++ b/docs/html/distribute/engage/widgets.jd @@ -1,5 +1,5 @@ page.title=Build Useful Widgets -page.metaDescription=Use widgets to remind users about important information in your apps and games, even when your apps are closed. +page.metaDescription=Use home screen widgets to remind users about important information in your apps and games, even when your apps are closed. page.tags="" page.image=/images/gp-engage-0.jpg diff --git a/docs/html/distribute/monetize/ads.jd b/docs/html/distribute/monetize/ads.jd index bcb1e52..9a847ff 100644 --- a/docs/html/distribute/monetize/ads.jd +++ b/docs/html/distribute/monetize/ads.jd @@ -10,17 +10,32 @@ page.image=/distribute/images/advertising.png </div> <p> - In-app advertising offers a quick and easy way to incorporate a monetization - option into both your <a href= + Ads can be a quick and easy way to earn more from your <a href= "{@docRoot}distribute/monetize/freemium.html">freemium</a>, <a href= "{@docRoot}distribute/monetize/premium.html">premium</a>, and <a href= - "{@docRoot}distribute/monetize/subscriptions.html">subscription</a> apps. </p> + "{@docRoot}distribute/monetize/subscriptions.html">subscription</a> apps. + AdMob and the Google Mobile Ads SDK let you add advertising to your apps with + just a few lines of code. +</p> + +<p> + The question is: which model gets the best results for your app? Google's ad + tools are made to help you figure out what combination works best for both + your audience and your bottom line. </p> -<p>Using <a href= +<p>Start by linking your AdMob and Google + Analytics accounts to get better insights and more earning power: for + instance, AdMob can promote in-app purchases to the people who buy them most + often, while showing income-generating ads to those less likely to buy right + now. +</p> + +<p> + Using <a href= "http://www.google.com/ads/admob/monetize.html#subid=us-en-et-dac">AdMob</a> - and the <a href="{@docRoot}google/play-services/ads.html">Google - Mobile Ads SDK</a> included in Google Play Services, you’re able to add - advertising into your apps, with just a few lines of code. + and the <a href="{@docRoot}google/play-services/ads.html">Google Mobile Ads + SDK</a> included in Google Play Services, you’re able to add advertising into + your apps, with just a few lines of code. </p> <p> @@ -30,36 +45,33 @@ page.image=/distribute/images/advertising.png <ul> <li> <p> - <strong>Placement within your apps</strong> — Well placed ads will - optimize your revenue by making it more likely that users will ‘click - through’. Poorly placed ads can result in low click-through rates, and in - the worse cases poor rating and users rapidly abandoning your apps. You - can get advice on how to best place ads from the developer training on - <a href= - "{@docRoot}training/monetization/ads-and-ux.html">using - ads</a>. + <strong>Placement within your apps</strong> — Well-placed ads make + it more likely that users will click through and convert. Poorly-placed + ads lead to lower click-through rates, and even poor ratings and users + abandoning your apps. Our <a href= + "{@docRoot}training/monetization/ads-and-ux.html">developer training</a> + on using ads shows some of the best ways to place ads. </p> </li> <li> <p> <strong>Ad formats</strong> — Every app offers a different type of - experience for users, so it’s important to consider the format of ads - you’re using to ensure it’s compatible with the experience. While banner - ads may work well for a flashlight utility app, an immersive gaming app - may benefit more from a video interstitial. Mismatched ad formats may - negatively affect your users’ experience and ad revenue, so try to select - formats that fit well with the content and flow of your apps. + experience for users, so it’s important that your ad formats match that + experience. While banner ads may work well for a flashlight utility app, + an immersive gaming app may benefit more from a video interstitial. + Mismatched ad formats can make users unhappy and leave money on the + table. </p> </li> <li> <p> - <strong>Maximizing your performance</strong> — Ensure you’re optimizing - your advertising revenue by maximizing your CPMs <em>and</em> fill rate. - Often ad providers will cite very high CPMs but will have a low fill rate - that can severely decrease your effective CPM, so look at both of these - figures. Also consider using a <a href= + <strong>Maximizing your performance</strong> — Make sure you’re + optimizing your advertising revenue by maximizing your CPMs and fill + rate. Ad providers often cite their very high CPMs but don't mention low + fill rates that can severely decrease your effective CPM. Be sure to look + at both of these figures. Consider using a <a href= "https://support.google.com/admob/v2/answer/3063564?hl=en&ref_topic=3063091#subid=us-en-et-dac"> mediation</a> solution if you’d like to use multiple ad providers in your apps. Look for solutions that offer yield management or <a href= @@ -71,30 +83,43 @@ page.image=/distribute/images/advertising.png <li> <p> - <strong>Exercising control options</strong> — A variety of ads promoting a - broad selection of other services or apps may show up within you apps. - Depending on your goals and the type of experience you want to provide - your users, it may make sense to <a href= + <strong>Exercising control options</strong> — A variety of ads may + show up within your app. It may make sense to <a href= "https://support.google.com/admob/v2/answer/3150235?hl=enl#subid=us-en-et-dac"> - block</a> certain advertisements from appearing. Some developers don’t - want apps in a similar category showing to their users, but some don’t - mind. + block</a> certain of those advertisements from appearing, depending on + your goals and the type of experience you want to provide. Some + developers, for instance, don’t want ads for apps in their same category + showing to their users, while others don’t mind at all. </p> </li> <li> <p> - <strong>Cross promoting your other apps</strong> — Ads can be used for - more than just earning revenue. Consider using <a href= + <strong>Cross promoting your other apps</strong> — Ads can do more + than earn revenue. Consider running <a href= "https://support.google.com/admob/v2/answer/3210452?hl=en#subid=us-en-et-dac"> - house ads</a> within your apps to create awareness and promote your - entire portfolio of apps. When launching new apps, an easy way to quickly - attract users is to promote directly to your existing customers. + house ads</a> within your apps to promote other apps in your portfolio. + When you launch a new app, this kind of promotion is a free and easy way + to attract new users quickly. </p> </li> </ul> <p> + Don't forget that paid channels like AdWords and YouTube can help you cast a + wider net by reaching targeted audiences outside the app ecosystem. They're a + great way to find new users at a price that you control. <a href= + "https://support.google.com/adwords/answer/2549053">Learn more</a>. +</p> + +<p> + To start monetizing with ads, sign up for AdMob and integrate the Google + Mobile Ads SDK into your apps. If you also need to manage direct deals with + advertisers, consider using DoubleClick for Publishers Small Business. +</p> + + +<p> To start monetizing with ads sign up for <a href= "http://www.google.com/ads/admob/#subid=us-en-et-dac">AdMob</a> and integrate the <a href="https://developers.google.com/mobile-ads-sdk/download">Google diff --git a/docs/html/distribute/users/index.jd b/docs/html/distribute/users/index.jd index 77ef609..a810f36 100644 --- a/docs/html/distribute/users/index.jd +++ b/docs/html/distribute/users/index.jd @@ -14,8 +14,8 @@ nonavpage=true <div class="resource-widget resource-flow-layout landing col-16" data-query="collection:distribute/users" - data-cardSizes="6x6, 6x6, 6x6, 9x6, 9x6, 6x6, 6x6, 6x6" - data-maxResults="8"> + data-cardSizes="6x6" + data-maxResults="6"> </div> <h3>Related resources</h3> diff --git a/docs/html/distribute/users/promote-with-ads.jd b/docs/html/distribute/users/promote-with-ads.jd new file mode 100644 index 0000000..1e28ae1 --- /dev/null +++ b/docs/html/distribute/users/promote-with-ads.jd @@ -0,0 +1,45 @@ +page.title=Promote Your App with Ads +page.metaDescription=Promote your app through AdMob, AdWords, and YouTube to find new users at the right moment. +page.image=/images/gp-ads-console.jpg +page.tags="users, ads, analytics" + +@jd:body + +<p> + AdMob is Google's advertising platform for mobile apps. You can use it to + monetize your app and promote your apps, and you can link your Google + Analytics account to AdMob so you can analyze your apps — all in one + place. +</p> + +<p> + <a href="http://www.google.com/ads/admob/">AdMob</a> is the largest mobile ad + app network. But you get more than just massive scale: AdMob will soon help + you find the right users in related apps. If your app is for bicycling, AdMob + can promote your app on other fitness and cycling-related apps worldwide. + <a href= + "https://apps.admob.com/admob/signup?subid=us-en-et-dac&_adc=ww-ww-et-admob2&hl=en"> + Sign up for AdMob</a>. +</p> + +<p> + AdMob also offers new solutions to help you achieve app-related goals such as + downloads, re-engagement and in-app purchases using Google search and the + Google Display Network. These solutions include streamlined campaign creation + flows and tools to track performance across the entire app lifecycle. + <a href="https://support.google.com/adwords/answer/2549053?hl=en">Learn + More</a>. +</p> +<div style="margin-top:2em;"> + <img src="{@docRoot}images/gp-ads-console.jpg"> +</div> + +<div class="headerLine"> +<h2 id="related-resources">Related Resources</h2> +</div> + +<div class="resource-widget resource-flow-layout col-13" + data-query="collection:distribute/users/promotewithads" + data-sortOrder="-timestamp" + data-cardSizes="9x3" + data-maxResults="6"></div>
\ No newline at end of file diff --git a/docs/html/distribute/users/users_toc.cs b/docs/html/distribute/users/users_toc.cs index a2437a6..1f173cb 100644 --- a/docs/html/distribute/users/users_toc.cs +++ b/docs/html/distribute/users/users_toc.cs @@ -28,7 +28,12 @@ </a> </div> </li> - + <li class="nav-section"> + <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/users/promote-with-ads.html"> + <span class="en">Promote with Ads</span> + </a> + </div> + </li> </ul> diff --git a/docs/html/guide/practices/verifying-apps-art.jd b/docs/html/guide/practices/verifying-apps-art.jd new file mode 100644 index 0000000..0eedfaf --- /dev/null +++ b/docs/html/guide/practices/verifying-apps-art.jd @@ -0,0 +1,296 @@ +page.title=Verifying App Behavior on the Android Runtime (ART) +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Quickview</h2> + <ul> + <li>The new Android runtime (ART) is available on some of the newest Android + devices, though all of them currently have Dalvik as the default + runtime.</li> + <li>App developers should make sure their apps are compatible with ART, + especially if you use JNI to run native code or if you use certain tools + that produce non-standard code (such as some obfuscators).</li> + </ul> + + <h2 id="Contents">In this document</h2> + <ol> + <li><a href="#GC_Migration">Addressing Garbage Collection (GC) Issues</a></li> + <li><a href="#JNI_Issues">Preventing JNI Issues</a> + <ol> + <li><a href="#JNI_and_GC">Checking JNI code for garbage-collection + issues</a></li> + <li><a href="#Error_Handling">Error handling</a></li> + <li><a href="#Object_Model_Changes">Object model changes</a></li> + </ol> + </li> + <li><a href="#Stack_Size">Preventing Stack Size Issues</a></li> + <li><a href="#AOT_Fails">Fixing AOT Compilation Issues</a></li> + <li><a href="#Reporting_Problems">Reporting Problems</a></li> + </ol> + <h2>See also</h2> + <ol> + <li><a href="http://source.android.com/devices/tech/dalvik/art.html">Introducing ART</a></li> + <li><a +href="http://android-developers.blogspot.com/2011/07/debugging-android-jni-with-checkjni.html">Debugging +Android JNI with CheckJNI</a></li> + </ol> +</div> +</div> + +<p>With Android 4.4, we are beginning to roll out a new Android runtime, +<strong>ART</strong>. This runtime offers a number of new features that improve +performance and smoothness of the Android platform and apps. (You can find more +information about ART's new features in <a +href="http://source.android.com/devices/tech/dalvik/art.html">Introducing +ART</a>.)</p> + +<p>Currently, ART is available on a number of Android 4.4 devices, such as the +Nexus 4, Nexus 5, Nexus 7, and Google Play edition devices. +At this time, all devices still use Dalvik as the default runtime. We encourage +you to test your apps for ART compatibility and to take advantage of ART's new +features. However, for the time being, you should also take care to maintain +compatibility with Dalvik.</p> + +<p>This document lets you know about things to watch for when migrating an +existing app to be compatible with ART. Most apps should just work when +running with ART. However, some techniques that work on Dalvik do not work on +ART. This document discusses some of these issues.</p> + +<h2 id="GC_Migration">Addressing Garbage Collection (GC) Issues</h2> + +<p>Under Dalvik, apps frequently find it useful to explicitly call {@link +java.lang.System#gc() System.gc()} to prompt garbage collection (GC). This should be +far less necessary with ART, particularly if you're invoking garbage collection +to prevent <a +href="{@docRoot}/tools/debugging/debugging-memory.html#LogMessages"><code>GC_FOR_ALLOC</code></a>-type +occurrences or to reduce fragmentation. You can verify which runtime is in use +by calling {@link java.lang.System#getProperty(java.lang.String) +System.getProperty("dalvik.vm.version")}. If ART is in use, the property's value +is <code>"2.0.0"</code> or higher.</p> + +<p>Furthermore, a compacting garbage collector is under development in the <a +href="https://source.android.com">Android Open-Source Project (AOSP)</a> to +improve memory management. Because of this, you should avoid using techniques +that are incompatible with compacting GC (such as saving pointers to object +instance data). This is particularly important for apps that make use of the +Java Native Interface (JNI). For more information, see <a +href="#JNI_Issues">Preventing JNI Issues</a>.</p> + +<h2 id="JNI_Issues">Preventing JNI Issues</h2> + +<p>ART's JNI is somewhat stricter than Dalvik's. It is an especially good idea +to use CheckJNI mode to catch common problems. If your app makes use of C/C++ +code, you should review the following article:</p> + +<p><a +href="http://android-developers.blogspot.com/2011/07/debugging-android-jni-with-checkjni.html">Debugging +Android JNI with CheckJNI</a></p> + +<h3 id="JNI_and_GC">Checking JNI code for garbage-collection issues</h3> + +<p>ART has a compacting garbage collector under development on the +Android Open Source Project (AOSP). Once the compacting garbage collector is in +use, objects may be moved in memory. If you use C/C++ code, do not +perform operations that are incompatible with compacting GC. We have enhanced +CheckJNI to identify some potential issues (as described in <a +href="http://android-developers.blogspot.com/2011/11/jni-local-reference-changes-in-ics.html">JNI +Local Reference Changes in ICS</a>).</p> + +<p>One area to watch for in particular is the use of +<code>Get...ArrayElements()</code> and <code>Release...ArrayElements()</code> +functions. In runtimes with non-compacting GC, the +<code>Get...ArrayElements()</code> functions typically return a reference to the +actual memory backing the array object. If you make a change to one of the +returned array elements, the array object is itself changed (and the arguments +to <code>Release...ArrayElements()</code> are usually ignored). However, if +compacting GC is in use, the <code>Get...ArrayElements()</code> functions may +return a copy of the memory. If you misuse the reference when compacting GC is +in use, this can lead to memory corruption or other problems. For example:</p> + +<ul> + + <li>If you make any changes to the returned array elements, you must call the + appropriate <code>Release...ArrayElements()</code> function when you are done, + to make sure the changes you made are correctly copied back to the underlying + array object.</li> + + <li>When you release the memory array elements, you must use the appropriate + mode, depending on what changes you made: + + <ul> + + <li>If you did not make any changes to the array elements, use + <code>JNI_ABORT</code> mode, which releases the memory without copying + changes back to the underlying array object.</li> + + <li>If you made changes to the array, and do not need the reference any + more, use code <code>0</code> (which updates the array object and frees + the copy of the memory).</li> + + <li>If you made changes to the array that you want to commit, and you want + to keep the copy of the array, use <code>JNI_COMMIT</code> (which updates + the underlying array object and retains the copy).</li> + + </ul> + + </li> + + <li>When you call <code>Release...ArrayElements()</code>, return the same + pointer that was originally returned by <code>Get...ArrayElements()</code>. For + example, it's not safe to increment the original pointer (to scan through the + returned array elements) then pass the incremented pointer to + <code>Release...ArrayElements()</code>. Passing this modified pointer can cause + the wrong memory to be freed, resulting in memory corruption.</li> + +</ul> + +<h3 id="Error_Handling">Error handling</h3> + +<p>ART's JNI throws errors in a number of cases where Dalvik didn’t. (Once +again, you can catch many such cases by testing with CheckJNI.)</p> + +<p>For example, if <code>RegisterNatives</code> is called with a method that +does not exist (perhaps because the method was removed by a tool such as +<strong>ProGuard</strong>), ART now properly throws {@link +java.lang.NoSuchMethodError}:</p> + +<pre class="no-pretty-print"> +08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main +08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError: + no static or non-static method + "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I" +08-12 17:09:41.082 13823 13823 E AndroidRuntime: + at java.lang.Runtime.nativeLoad(Native Method) +08-12 17:09:41.082 13823 13823 E AndroidRuntime: + at java.lang.Runtime.doLoad(Runtime.java:421) +08-12 17:09:41.082 13823 13823 E AndroidRuntime: + at java.lang.Runtime.loadLibrary(Runtime.java:362) +08-12 17:09:41.082 13823 13823 E AndroidRuntime: + at java.lang.System.loadLibrary(System.java:526) +</pre> + +<p>ART also logs an error (visible in logcat) if <code>RegisterNatives</code> is +called with no methods:</p> + +<pre class="no-pretty-print"> +W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native +methods for <classname> +</pre> + +<p>In addition, the JNI functions <code>GetFieldID()</code> and +<code>GetStaticFieldID()</code> now properly throw {@link java.lang.NoSuchFieldError} +instead of simply returning null. Similarly, <code>GetMethodID()</code> and +<code>GetStaticMethodID()</code> now properly throw {@link java.lang.NoSuchMethodError}. +This can lead to CheckJNI failures because of the unhandled exceptions or the +exceptions being thrown to Java callers of native code. This makes it +particularly important to test ART-compatible apps with CheckJNI mode.</p> + +<p>ART expects users of the JNI <code>CallNonvirtual...Method()</code> methods +(such as <code>CallNonvirtualVoidMethod()</code>) to use the method's declaring +class, not a subclass, as required by the JNI specification.</p> + +<h2 id="Stack_Size">Preventing Stack Size Issues</h2> + +<p>Dalvik had separate stacks for native and Java code, with a default Java +stack size of 32KB and a default native stack size of 1MB. ART has a unified +stack for better locality. Ordinarily, the ART {@link java.lang.Thread} stack +size should be approximately the same as for Dalvik. However, if you explicitly +set stack sizes, you may need to revisit those values for apps running in +ART.</p> + +<ul> + + <li>In Java, review calls to the {@link + java.lang.Thread#Thread(java.lang.ThreadGroup, java.lang.Runnable, + java.lang.String, long) Thread} constructor that specify an explicit stack + size. For example, you will need to increase the size if {@link + java.lang.StackOverflowError} occurs.</li> + + <li>In C/C++, review use of <code>pthread_attr_setstack()</code> and + <code>pthread_attr_setstacksize()</code> for threads that also run Java code via + JNI. Here is an example of the error logged when an app attempts to call JNI + <code>AttachCurrentThread()</code> when the pthread size is too small: + +<pre class="no-pretty-print">F/art: art/runtime/thread.cc:435] + Attempt to attach a thread with a too-small stack (16384 bytes)</pre> + </li> + +</ul> + +<h2 id="Object_Model_Changes">Object model changes</h2> + +<p>Dalvik incorrectly allowed subclasses to override package-private methods. +ART issues a warning in such cases:</p> + +<pre class="no-pretty-print"> +Before Android 4.1, method void com.foo.Bar.quux() +would have incorrectly overridden the package-private method in +com.quux.Quux +</pre> + +<p>If you intend to override a class's method in a different package, declare the +method as <code>public</code> or <code>protected</code>.</p> + +<p>{@link java.lang.Object} now has private fields. Apps that reflect on fields +in their class hierarchies should be careful not to attempt to look at the +fields of {@link java.lang.Object}. For example, if you are iterating up a class +hierarchy as part of a serialization framework, stop when + +<pre>Class.getSuperclass() == java.lang.Object.class</pre> + +instead of continuing until the method returns <code>null</code>.</p> + +<p>Proxy {@link +java.lang.reflect.InvocationHandler#invoke(java.lang.Object,java.lang.reflect.Method,java.lang.Object[]) +InvocationHandler.invoke()} now receives <code>null</code> if there are no +arguments instead of an empty array. This behavior was documented previously but +not correctly handled in Dalvik. Previous versions of <a +href="https://code.google.com/p/mockito/">Mockito</a> have difficulties with +this, so use an updated Mockito version when testing with ART.</p> + +<h2 id="AOT_Fails">Fixing AOT Compilation Issues</h2> + +<p>ART's Ahead-Of-Time (AOT) Java compilation should work for all standard Java +code. Compilation is performed by ART's +<code>dex2oat</code> tool; if you encounter any issues related to +<code>dex2oat</code> at install time, let us know (see <a +href="#Reporting_Problems">Reporting Problems</a>) so we can fix them as quickly +as possible. A couple of issues to note:</p> + +<ul> + + <li>ART does tighter bytecode verification at install time than Dalvik does. + Code produced by the Android build tools should be fine. However, some + post-processing tools (especially tools that perform obfuscation) may produce + invalid files that are tolerated by Dalvik but rejected by ART. We have been + working with tool vendors to find and fix such issues. In many cases, getting + the latest versions of your tools and regenerating the DEX files can fix these + problems.</li> + + <li>Some typical problems that are flagged by the ART verifier include: + <ul> + <li>invalid control flow</li> + <li>unbalanced <code>moniterenter</code>/<code>moniterexit</code></li> + <li>0-length parameter type list size</li> + </ul> + </li> + + <li>Some apps have dependencies on the installed <code>.odex</code> file + format in <code>/system/framework</code>, <code>/data/dalvik-cache</code>, or + in {@link dalvik.system.DexClassLoader}’s optimized output directory. These + files are now ELF files and not an extended form of DEX files. While ART tries + to follow the same naming and locking rules as Dalvik, apps should not depend + on the file format; the format is subject to change without notice.</li> + + + +<h2 id="Reporting_Problems">Reporting Problems</h2> + +<p>If you run into any issues that aren’t due to app JNI issues, report +them via the Android Open Source Project Issue Tracker at <a +href="https://code.google.com/p/android/issues/list">https://code.google.com/p/android/issues/list</a>. +Include an <code>"adb bugreport"</code> and a link to the app in the Google +Play store if available. Otherwise, if possible, attach an APK that reproduces +the issue. Note that issues (including attachments) are publicly +visible.</p> diff --git a/docs/html/images/gp-ads-console.jpg b/docs/html/images/gp-ads-console.jpg Binary files differnew file mode 100644 index 0000000..158e31d --- /dev/null +++ b/docs/html/images/gp-ads-console.jpg diff --git a/docs/html/images/gp-ads-linking2.jpg b/docs/html/images/gp-ads-linking2.jpg Binary files differnew file mode 100644 index 0000000..0c2f731 --- /dev/null +++ b/docs/html/images/gp-ads-linking2.jpg diff --git a/docs/html/images/gp-analytics.jpg b/docs/html/images/gp-analytics.jpg Binary files differnew file mode 100644 index 0000000..e1a92c7 --- /dev/null +++ b/docs/html/images/gp-analytics.jpg diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js index 8a4ac47..5ee01f3 100644 --- a/docs/html/jd_collections.js +++ b/docs/html/jd_collections.js @@ -49,7 +49,8 @@ var RESOURCE_COLLECTIONS = { "distribute/users/your-listing.html", "distribute/users/build-buzz.html", "distribute/users/build-community.html", - "distribute/users/expand-to-new-markets.html" + "distribute/users/expand-to-new-markets.html", + "distribute/users/promote-with-ads.html" ] }, "distribute/engagelanding": { @@ -60,6 +61,7 @@ var RESOURCE_COLLECTIONS = { "distribute/engage/easy-signin.html", "distribute/engage/deep-linking.html", "distribute/engage/game-services.html", + "distribute/engage/analytics.html", "distribute/engage/app-updates.html", "distribute/engage/community.html", "distribute/engage/video.html" @@ -214,6 +216,13 @@ var RESOURCE_COLLECTIONS = { "distribute/stories/localization.html" ] }, + "distribute/users/promotewithads": { + "title": "", + "resources": [ + "http://www.google.com/ads/admob/#subid=us-en-et-dac", + "distribute/essentials/optimizing-your-app.html" + ] + }, "distribute/users/buildbuzz": { "title": "", "resources": [ @@ -394,6 +403,14 @@ var RESOURCE_COLLECTIONS = { "http://play.google.com/about/developer-content-policy.html" ] }, + "distribute/engage/analytics": { + "title": "", + "resources": [ + "http://www.google.com/analytics/mobile/", + "http://android-developers.blogspot.com/2013/10/improved-app-insight-by-linking-google.html", + "https://developers.google.com/analytics/devguides/collection/android/" + ] + }, "distribute/engage/widgets": { "title": "", "resources": [ diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js index f26b747..d8db5bf 100644 --- a/docs/html/jd_extras.js +++ b/docs/html/jd_extras.js @@ -726,6 +726,21 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([ "tags": [ "#engagement", ], + "url": "http://www.google.com/analytics/mobile/", + "timestamp": 1383243492000, + "image": "http://www.google.com//analytics/images/heros/mobile-index.jpg", + "title": "Google Mobile App Analytics", + "summary": "Mobile App Analytics measures what matters most at all key stages: from first discovery and download to in-app purchases. ", + "keywords": ["analytics,user behavior"], + "type": "guide", + "titleFriendly": "" + }, + { + "lang": "en", + "group": "", + "tags": [ + "#engagement", + ], "url": "https://developers.google.com/app-indexing/", "timestamp": 1383243492000, "image": "https://developers.google.com/app-indexing/images/allthecooks_srp.png", @@ -1101,4 +1116,17 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([ "type": "Google+", "titleFriendly": "" }, + { + "lang": "en", + "group": "", + "tags": ["analytics"], + "url": "https://developers.google.com/analytics/devguides/collection/android/", + "timestamp": null, + "image": "https://developers.google.com/analytics/images/home/gear-logo.png", + "title": "Google Mobile App Analytics SDK", + "summary": "The Google Analytics for Mobile Apps SDKs make it easy for you to implement Google Analytics in your mobile application.", + "keywords": ["analytics, user behavior"], + "type": "sdk", + "titleFriendly": "" + } ]);
\ No newline at end of file diff --git a/docs/html/sdk/exploring.jd b/docs/html/sdk/exploring.jd index 7749060..b34c1cf 100644 --- a/docs/html/sdk/exploring.jd +++ b/docs/html/sdk/exploring.jd @@ -5,163 +5,6 @@ walkthru=1 @jd:body -<p>The Android SDK is composed of modular packages that you can download separately using -the Android SDK Manager. For example, when the SDK Tools are updated or a new version of -the Android platform is released, you can use the SDK Manager to quickly download them to -your environment. Simply follow the procedures described in <a -href="{@docRoot}sdk/installing/adding-packages.html">Adding Platforms and Packages</a>.</p> - -<p>There are several different packages available for the Android SDK. The table below describes -most of the available packages and where they're located once you download them.</p> - - -<h2 id="Packages">Available Packages</h2> - - -<table> - <tr><th>Package</th><th>Description</th><th>File Location</th></tr> - <tr> - <td><a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools</a></td> - <td>Contains tools for debugging and testing, plus other -utilities that are required to develop an app. If you've just installed the SDK starter package, -then you already have the latest version of this package. Make sure you keep this up to date.</td> - <td>{@code <sdk>/tools/}</td></tr> - <tr><td>SDK Platform-tools</td> - <td>Contains platform-dependent tools for developing and debugging -your application. These tools support the latest features of the Android platform and are typically -updated only when a new platform becomes available. These tools are always backward compatible with -older platforms, but you must be sure that you have the latest version of these tools when you -install a new SDK platform.</td> - <td>{@code <sdk>/platform-tools/}</td> - </tr> - - <tr> - <td>Documentation</td> - <td>An offline copy of the latest documentation for the Android -platform APIs.</td> - <td>{@code <sdk>/docs/}</td> - </tr> - <tr><td>SDK Platform</td> - <td>There's one SDK Platform available for each version of Android. It includes an {@code -android.jar} file with a fully compliant Android library. In order to build an Android app, you must -specify an SDK platform as your build target.</td> - <td>{@code <sdk>/platforms/<android-version>/}</td> - </tr> - <tr> - <td>System Images</td> - <td>Each platform version offers one or more different system images (such as for ARM -and x86). The Android emulator requires a system image to operate. You should always test your -app on the latest version of Android and using the emulator with the latest system image is a -good way to do so.</td> - <td>{@code <sdk>/platforms/<android-version>/}</td> - </tr> - <tr> - <td>Sources for Android SDK</td> - <td>A copy of the Android platform source code that's useful for -stepping through the code while debugging your app.</td> - <td>{@code <sdk>/sources/}</td> - </tr> - <tr> - <td><a href="{@docRoot}tools/samples/index.html">Samples for SDK</a></td> - <td>A collection of sample apps that demonstrate a variety of the -platform APIs. These are a great resource to browse Android app code. The API Demos app in -particular provides a huge number of small demos you should explore.</td> - <td>{@code <sdk>/platforms/<android-version>/samples/}</td> - </tr> - <tr> - <td><a href="http://developers.google.com/android">Google APIs</a></td> - <td>An SDK add-on that provides both a platform you can use to develop an app -using special Google APIs and a system image for the emulator so you can test your app using the -Google APIs.</td> - <td>{@code <sdk>/add-ons/}</td> - </tr> - - <tr> - <td><a href="{@docRoot}tools/support-library/index.html">Android Support</a></td> - <td>A static library you can include in your app sources in order to use powerful -APIs that aren't available in the standard platform. For example, the support library -contains versions of the {@link android.support.v4.app.Fragment} class that's compatible with -Android 1.6 and higher (the class was originally introduced in Android 3.0) and the {@link -android.support.v4.view.ViewPager} APIs that allow you to easily build a side-swipeable UI.</td> - <td>{@code <sdk>/extras/android/support/}</td> - </tr> - <tr> - <td><a href="{@docRoot}google/play/billing/index.html">Google Play Billing</a></td> - <td>Provides the static libraries and samples that allow you to -integrate billing services in your app with Google Play.</td> - <td>{@code <sdk>/extras/google/}</td> - </tr> - <tr> - <td><a href="{@docRoot}google/play/licensing/index.html">Google Play Licensing</a></td> - <td>Provides the static libraries and samples that allow you to perform license verification for -your app when distributing with Google Play.</td> - <td>{@code <sdk>/extras/google/}</td> - </tr> -</table> - -<p>The above table is not comprehensive and you can <a -href="#AddingSites">add new sites</a> to download additional packages from third-parties.</p> - -<p>In some cases, an SDK package may require a specific minimum revision of -another package or SDK tool. For example, there may be a dependency between the ADT Plugin for -Eclipse and -the SDK Tools package. When you install the SDK Tools -package, you should also upgrade to the required version of ADT (if you -are developing in Eclipse). In this case, the major version number for your ADT plugin should -always match the revision number of your SDK Tools (for example, ADT 8.x requires SDK Tools r8). -</p> - -<p>The development tools will notify you with debug warnings if there is dependency that you need to -address. The Android SDK Manager also enforces dependencies by requiring that you download any -packages that are needed by those you have selected.</p> - - - - - -<h2 id="AddingSites">Adding New Sites</h2> - -<p>By default, <strong>Available Packages</strong> displays packages available from the -<em>Android Repository</em> and <em>Third party Add-ons</em>. You can add other sites that host -their own Android SDK add-ons, then download the SDK add-ons -from those sites.</p> - -<p>For example, a mobile carrier or device manufacturer might offer additional -API libraries that are supported by their own Android-powered devices. In order -to develop using their libraries, you must install their Android SDK add-on, if it's not already -available under <em>Third party Add-ons</em>. </p> - -<p>If a carrier or device manufacturer has hosted an SDK add-on repository file -on their web site, follow these steps to add their site to the Android SDK -Manager:</p> - -<ol> - <li>Select <strong>Available Packages</strong> in the left panel.</li> - <li>Click <strong>Add Add-on Site</strong> and enter the URL of the -<code>repository.xml</code> file. Click <strong>OK</strong>.</li> -</ol> -<p>Any SDK packages available from the site will now be listed under a new item named -<strong>User Add-ons</strong>.</p> - - - - -<h2 id="troubleshooting">Troubleshooting</h2> - -<p><strong>Problems connecting to the SDK repository</strong></p> - -<p>If you are using the Android SDK Manager to download packages and are encountering -connection problems, try connecting over http, rather than https. To switch the -protocol used by the Android SDK Manager, follow these steps: </p> - -<ol> - <li>With the Android SDK Manager window open, select "Settings" in the - left pane. </li> - <li>On the right, in the "Misc" section, check the checkbox labeled "Force - https://... sources to be fetched using http://..." </li> - <li>Click <strong>Save & Apply</strong>.</li> -</ol> - diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd index e7f78e8..c6dbb90 100644 --- a/docs/html/sdk/index.jd +++ b/docs/html/sdk/index.jd @@ -206,12 +206,10 @@ This is the Android Software Development Kit License Agreement <div id="next-steps" style="display:none;position:absolute;width:inherit"> - <p>Now that you've downloaded the Android SDK, you don't need to return here - for SDK updates. The SDK tools allow you to - install additional packages and future updates from the SDK Manager.</p> - <p>For instructions about setting up your Android SDK for the first time, - read <a id="next-link" href="{@docRoot}sdk/installing/bundle.html">Setting - Up the ADT Bundle</a>.</p> + <p>You're just a few steps away from building apps for Android!</p> + <p>In a moment, you'll be redirected to <a + id="next-link" href="{@docRoot}sdk/installing/index.html">Installing the + Android SDK</a>.</p> </div><!-- end next-steps --> diff --git a/docs/html/sdk/installing/adding-packages.jd b/docs/html/sdk/installing/adding-packages.jd index bba936e..c38c927 100644 --- a/docs/html/sdk/installing/adding-packages.jd +++ b/docs/html/sdk/installing/adding-packages.jd @@ -1,63 +1,222 @@ -page.title=Adding Platforms and Packages +page.title=Adding SDK Packages @jd:body +<style> +ol.large { + margin-left:0; +} +ol.large > li { + list-style-position: inside; + list-style-type:none; + margin:30px 0 0 0; + padding:30px 20px; + background:#eee; +} +ol.large > li:nth-child(odd) { +} +ol.large > li:before { + display:inline; + left:-40px; + float:left; + width:20px; + font-size:20px; + line-height:20px; +} +ol.large > li > h2 { + font-size:20px; + line-height:20px; + padding:0 0 0 20px; + margin:0 0 20px 0; + display:inline-block; + font-weight:normal; +} +ol.large > li:nth-child(1):before { + content:"1. "; +} +ol.large > li:nth-child(2):before { + content:"2. "; +} +ol.large > li:nth-child(3):before { + content:"3. "; +} +ol.large > li:nth-child(4):before { + content:"4. "; +} +ol.large > li:nth-child(5):before { + content:"5. "; +} +ol.large > li:nth-child(6):before { + content:"6. "; +} +</style> -<p>The Android SDK separates tools, platforms, and other components into packages you can - download using the Android SDK Manager. The original -SDK package you've downloaded includes only the SDK Tools. To develop an Android app, -you also need to download at least one Android platform and the latest SDK Platform-tools.</p> -<ol> -<li>Launch the SDK Manager. -<p>If you've used the Windows installer to install the SDK tools, you should already have the -Android SDK Manager open. Otherwise, you can launch the Android SDK Manager in one of the following -ways:</p> +<p> +By default, the Android SDK does not include everything you need to start developing. +The SDK separates tools, platforms, and other components into packages you can +download as needed using the +<a href="{@docRoot}tools/help/sdk-manager.html">Android SDK Manager</a>. +So before you can start, there are a few packages you should add to your Android SDK.</p> + +<p>To start adding packages, launch the Android SDK Manager in one of the following ways:</p> <ul> - <li>On Windows, double-click the <code>SDK Manager.exe</code> file at the root of the Android -SDK directory.</li> - <li>On Mac or Linux, open a terminal and navigate to the <code>tools/</code> directory in the -Android SDK, then execute <code>android sdk</code>.</li> + <li>In Eclipse or Android Studio, click <strong>SDK Manager</strong> +<img src="{@docRoot}images/tools/sdk-manager-studio.png" +style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li> + <li>If you're not using Eclipse or Android Studio: + <ul> + <li>Windows: Double-click the <code>SDK Manager.exe</code> file at the root of the Android + SDK directory.</li> + <li>Mac/Linux: Open a terminal and navigate to the <code>tools/</code> directory in the + Android SDK, then execute <code>android sdk</code>.</li> + </ul> + </li> </ul> + +<ol class="large"> +<li> + <h2 class="norule">Get the latest SDK tools</h2> + +<img src="/images/sdk_manager_packages.png" alt="" width="350" style="float:right;margin-left:20px" /> + + <p>As a minimum when setting up the Android SDK, + you should download the latest tools and Android platform:</p> + <ol> + <li>Open the Tools directory and select: + <ul> + <li><strong>Android SDK Tools</strong></li> + <li><strong>Android SDK Platform-tools</strong></li> + <li><strong>Android SDK Build-tools</strong></li> + </ul> + </li> + <li>Open the first Android X.X folder (the latest version) and select: + <ul> + <li><strong>SDK Platform</strong></li> + <li>A system image for the emulator, such as <br> + <strong>ARM EABI v7a System Image</strong></li> + </ul> + </li> + <li>Click <strong>Install</strong>.</li> + </ol> </li> -<li>The SDK Manager shows all the SDK packages available for you to add to your Android SDK. -As a minimum configuration for your SDK, we recommend you install the following: -<ul> - <li>The latest Tools packages (check the <strong>Tools</strong> folder).</li> - <li>The latest version of Android (check the first <strong>Android</strong> folder).</li> - <li>The Android Support Library (open the <strong>Extras</strong> folder and check - <strong>Android Support Library</strong>).</li> -</ul> +<li> + <h2 class="norule">Get the support library for additional APIs</h2> -<p>Once you've chosen your packages, click <strong>Install</strong>. The Android SDK Manager -installs the selected packages into your Android SDK environment.</li> -</ol> + <div class="sidebox"> + <h3>Why use the support library?</h3> -<p>With these packages installed, you're ready to start developing. -To get started, read <a href="{@docRoot}training/basics/firstapp/index.html" ->Building Your First App</a>.</p> + <p>The support library is required for:</p> + <ul> + <li><a href="{@docRoot}wear/index.html">Android Wear</a></li> + <li><a href="{@docRoot}tv/index.html">Android TV</a></li> + <li><a href="{@docRoot}google/play-services/cast.html">Google Cast</a></li> + </ul> -<img src="/images/sdk_manager_packages.png" alt="" height="396" /> -<p class="img-caption"><strong>Figure 1.</strong> The Android SDK Manager shows the -SDK packages that are available, already installed, or for which an update is available.</p> + <p>The support library also provides these popular APIs:</p> + <ul> + <li><a href="{@docRoot}reference/android/support/v4/widget/DrawerLayout.html">Navigation + drawer</a></li> + <li><a href="{@docRoot}reference/android/support/v4/view/ViewPager.html">Swipe views</a></li> + <li><a href="{@docRoot}reference/android/support/v7/app/ActionBar.html">Backward-compatible + action bar</a></li> + </ul> + </div> + <p>The <a href="{@docRoot}tools/support-library/features.html">Android Support Library</a> + provides an extended set of APIs that are compatible with most versions of Android.</p> + <p>To download the support library:</p> + <ol> + <li>Open the <strong>Extras</strong> directory and select: + <ul> + <li><strong>Android Support Repository</strong></li> + <li><strong>Android Support Library</strong></li> + </ul> + </li> + <li>Click <strong>Install</strong>.</li> + </ol> -<h3>Additional information</h3> + <p> </p> + <p> </p> -<ul> - <li>For more information about using the SDK Manager and some of the available packages, -see the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> document.</li> - <li>This web site provides all information you need to develop Android apps, including <a -href="{@docRoot}design/index.html">design guidelines</a>, -<a href="{@docRoot}training/index.html">developer training</a>, <a -href="{@docRoot}reference/packages.html">API reference</a>, and information -about how you can <a href="{@docRoot}distribute/index.html">distribute your app</a>. We recommend -you begin by reading <a href="{@docRoot}training/basics/firstapp/index.html" ->Building Your First App</a>.</li> - <li>For additional resources about developing and distributing your app, see the -<a href="{@docRoot}support.html">Developer Support Resources</a>.</li> -</ul> +</li> + + +<li> + <h2 class="norule">Get Google Play services for even more APIs</h2> + + <div class="sidebox"> + <h3>Why use Google Play services?</h3> + + <p>The Google Play services APIs provide a variety of features and services for your Android + apps, such as:</p> + <ul> + <li><a href="{@docRoot}google/play-services/plus.html">User authentication</a></li> + <li><a href="{@docRoot}google/play-services/maps.html">Google Maps</a></li> + <li><a href="{@docRoot}google/play-services/cast.html">Google Cast</a></li> + <li><a href="{@docRoot}google/play-services/games.html">Games achievements and + leaderboards</a></li> + <li><a href="{@docRoot}google/play-services/index.html">And much more</a></li> + </ul> + </div> + + <p>To develop with Google APIs, you need the Google Play services package:</p> + <ol> + <li>Open the <strong>Extras</strong> directory and select: + <ul> + <li><strong>Google Repository</strong></li> + <li><strong>Google Play services</strong></li> + </ul> + </li> + <li>Click <strong>Install</strong>.</li> + </ol> + + <p class="note"><strong>Note:</strong> Google Play services APIs are not available on all + Android-powered devices, but are available on all devices with Google Play Store. To use these + APIs in the Android emulator, you must also install the the <strong>Google APIs</strong> + system image from the latest Android X.X directory in the SDK Manager.</p> +</li> + + + +<li> + <h2 class="norule">Build something!</h2> + +<p>With the above packages now in your Android SDK, you're ready to build apps +for Android. As new tools and other APIs become available, simply launch the SDK Manager + to download the new packages for your SDK.</p> + +<p>Here are a few options for how you should proceed:</p> + +<div class="cols" style="padding:10px 0"> +<div class="col-4"> +<h3>Get started</h3> +<p>If you're new to Android development, learn the basics of Android apps by following +the guide to <strong><a href="{@docRoot}training/basics/firstapp/index.html" +>Building Your First App</a></strong>.</p> + +</div> +<div class="col-4 box"> +<h3>Build for wearables</h3> +<p>If you're ready to start building apps for Android wearables, see the guide to +<strong><a href="{@docRoot}wear/preview/start.html">Building Apps for Android Wear</a></strong>.</p> + +</div> +<div class="col-4 box"> +<h3>Use Google APIs</h3> +<p>To start using Google APIs, such as Maps or +Play Game services, see the guide to +<strong><a href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services +APIs</a></strong>.</p> + +</div> +</div><!-- end cols --> + + +</li> + +</ol> diff --git a/docs/html/sdk/installing/bundle.jd b/docs/html/sdk/installing/bundle.jd index 1f7da55..22bdd11 100644 --- a/docs/html/sdk/installing/bundle.jd +++ b/docs/html/sdk/installing/bundle.jd @@ -1,45 +1,3 @@ page.title=Setting Up the ADT Bundle @jd:body - - -<p>The ADT Bundle provides everything you need to start developing apps, including -a version of the Eclipse IDE with built-in <b>ADT (Android Developer Tools)</b> to -streamline your Android app development. -If you haven't already, go download the <a href="{@docRoot}sdk/index.html" ->Android ADT Bundle</a>. (If you downloaded the SDK Tools only, for use with an -existing IDE, you should instead read -<a href="{@docRoot}sdk/installing/index.html">Setting Up an Existing IDE</a>.)</p> - -<h3>Install the SDK and Eclipse IDE</h3> -<ol> -<li>Unpack the ZIP file -(named {@code adt-bundle-<os_platform>.zip}) and save it to an appropriate location, -such as a "Development" directory in your home directory.</li> -<li>Open the {@code adt-bundle-<os_platform>/eclipse/} directory and launch -<strong>eclipse</strong>.</li> -</ol> - -<p>That's it! The IDE is already loaded with the Android Developer Tools plugin and -the SDK is ready to go. To start developing, read <a href="{@docRoot}training/basics/firstapp/index.html" ->Building Your First App</a>.</p> - -<p class="caution"><strong>Caution:</strong> Do not move any of the files or directories -from the {@code adt-bundle-<os_platform>} directory. If you move the {@code eclipse} -or {@code sdk} directory, ADT will not be able to locate the SDK and you'll -need to manually update the ADT preferences.</p> - -<h3>Additional information</h3> - -<p>As you continue developing apps, you may need to install additional versions -of Android for the emulator and other packages such as the library for -Google Play In-app Billing. To install more packages, use -the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>.</p> - -<p>Everything you need to develop Android apps is on this web site, including <a -href="{@docRoot}design/index.html">design guidelines</a>, -<a href="{@docRoot}training/index.html">developer training</a>, <a -href="{@docRoot}reference/packages.html">API reference</a>, and information -about how you can <a href="{@docRoot}distribute/index.html">distribute your app</a>. -For additional resources about developing and distributing your app, see the -<a href="{@docRoot}support.html">Developer Support Resources</a>.</p>
\ No newline at end of file diff --git a/docs/html/sdk/installing/index.jd b/docs/html/sdk/installing/index.jd index 6b63ba7..b6929bd 100644 --- a/docs/html/sdk/installing/index.jd +++ b/docs/html/sdk/installing/index.jd @@ -1,19 +1,213 @@ -page.title=Setting Up an Existing IDE +page.title=Installing the Android SDK @jd:body +<style> +.paging-links { + margin:0 0 80px; +} +.paging-links .next-page-link { + right:initial; +} +.procedure-box { + padding:20px 20px 5px; + margin-bottom:1em; + background:#eee; +} +</style> -<p>You should have already downloaded the <a href="{@docRoot}sdk/index.html#ExistingIDE" ->Android SDK Tools</a>. (If you downloaded the ADT Bundle, you should instead read -<a href="{@docRoot}sdk/installing/bundle.html">Setting Up the ADT Bundle</a>.)</p> -<p>The SDK Tools package is not the complete SDK environment. It includes only the core SDK tools, which you can -use to download the rest of the SDK packages (such as the latest system image).</p> +<!-- ################### ADT BUNDLE ####################### --> +<div id="adt" heading="Installing the Eclipse ADT Bundle" style="display:none"> -<div id="win" class="docs" style="display:none"> -<h3>Getting started on Windows</h3> +<p>The Eclipse ADT Bundle provides everything you need to start developing apps, including +the Android SDK tools and a version of the Eclipse IDE with built-in ADT +(Android Developer Tools) to streamline your Android app development.</p> + +<p>If you didn't download the Eclipse ADT bundle, go <a href="{@docRoot}sdk/index.html" +><b>download the Eclipse ADT bundle now</b></a>, or switch to the +<a href="{@docRoot}sdk/installing/index.html?pkg=studio">Android Studio +install</a> or <a href="{@docRoot}sdk/installing/index.html?pkg=tools">stand-alone SDK Tools +install</a> instructions</i>.</p> + +<div class="procedure-box"> +<p><b>To set up the ADT Bundle:</b></p> +<ol> +<li>Unpack the ZIP file +(named {@code adt-bundle-<os_platform>.zip}) and save it to an appropriate location, +such as a "Development" directory in your home directory.</li> +<li>Open the {@code adt-bundle-<os_platform>/eclipse/} directory and launch +<strong>Eclipse</strong>.</li> +</ol> + +<p class="caution"><strong>Caution:</strong> Do not move any of the files or directories +from the {@code adt-bundle-<os_platform>} directory. If you move the {@code eclipse/} +or {@code sdk/} directory, ADT will not be able to locate the SDK and you'll +need to manually update the ADT preferences.</p> +</div> + +<p>Eclipse with ADT is now ready and loaded with the Android developer tools, but there are still +a couple packages you should add to make your Android SDK complete.</p> + +<p class="paging-links"> +<a href="{@docRoot}sdk/installing/adding-packages.html" class="next-page-link"> +Continue: Adding SDK Packages</a></p> + + +</div> +<!-- ################ END ADT BUNDLE ##################### --> + + + + + + +<!-- ################ STUDIO ##################### --> +<div id="studio" heading="Installing Android Studio" style="display:none"> + +<p>Android Studio provides everything you need to start developing apps, including +the Android SDK tools and the Android Studio IDE (powered by IntelliJ) to +streamline your Android app development.</p> + +<p>If you didn't download Android Studio, go <a href="{@docRoot}sdk/installing/studio.html" +><b>download Android Studio now</b></a>, or switch to the +<a href="{@docRoot}sdk/installing/index.html?pkg=adt">Eclipse ADT +install</a> or <a href="{@docRoot}sdk/installing/index.html?pkg=tools">stand-alone SDK Tools +install</a> instructions.</p> + + +<p>Before you set up Android Studio, be sure you have installed +JDK 6 or greater (the JRE alone is not sufficient). To check if you +have JDK installed (and which version), open a terminal and type <code>javac -version</code>. +If the JDK is not available or the version is lower than 6, +<a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" class="external-link" +>go download JDK</a>.</p> + + +<div class="procedure-box"> + +<p id="instructions-toggle" +style="float:right;font-size:13px"><a href='' onclick='showAll();return false;' +>[ Show instructions for all platforms ]</a></p> + +<div class="win docs" style="display:none"> + +<p><b>To set up Android Studio on Windows:</b></p> + <ol> + <li>Launch the downloaded EXE file, {@code android-studio-bundle-<version>.exe}.</li> + <li>Follow the setup wizard to install Android Studio. + + <p>On some Windows systems, the launcher script does not find where Java is installed. + If you encounter this problem, + you need to set an environment variable indicating the correct location.</p> + <p>Select <strong>Start menu > Computer > System Properties > + Advanced System Properties</strong>. Then open <strong>Advanced tab > Environment + Variables</strong> and add a new system variable <code>JAVA_HOME</code> that points to + your JDK folder, for example <code>C:\Program Files\Java\jdk1.7.0_21</code>.</p> + </p> + </li> + + </ol> + + +<p>The individual tools and +other SDK packages are saved within the Android Studio application directory. +To access the tools directly, use a terminal to navigate into the application and locate +the {@code sdk/} directory. For example:</p> +<p><code>\Users\<user>\AppData\Local\Android\android-studio\sdk\</code></p> + + + +</div><!-- end windows --> + + +<div class="mac docs" style="display:none"> + +<p><b>To set up Android Studio on Mac OSX:</b></p> + <ol> + <li>Open the downloaded DMG file, {@code android-studio-bundle-<version>.dmg}.</li> + <li>Drag and drop Android Studio into the Applications folder. + <p> + Depending on your security settings, when you attempt to open Android Studio, you might + see a warning that says the package is damaged and should be moved to the trash. If this + happens, go to <strong>System Preferences > Security & Privacy</strong> and under + <strong>Allow applications downloaded from</strong>, select <strong>Anywhere</strong>. + Then open Android Studio again.</p> + </li> + </ol> + +<p>The individual tools and +other SDK packages are saved within the Android Studio application directory. +To access the tools directly, use a terminal to navigate into the application and locate +the {@code sdk/} directory. For example:</p> +<p><code>/Applications/Android\ Studio.app/sdk/</code></p> + + +</div><!-- end mac --> + + +<div class="linux docs" style="display:none"> + +<p><b>To set up Android Studio on Linux:</b></p> + + <ol> + <li>Unpack the downloaded Tar file, {@code android-studio-bundle-<version>.tgz}, into an appropriate + location for your applications. + <li>To launch Android Studio, navigate to the {@code android-studio/bin/} directory + in a terminal and execute {@code studio.sh}. + <p>You may want to add {@code android-studio/bin/} to your PATH environmental + variable so that you can start Android Studio from any directory.</p> + </li> + </ol> + +</div><!-- end linux --> +</div><!-- end procedure box --> + +<p>Android Studio is now ready and loaded with the Android developer tools, but there are still a +couple packages you should add to make your Android SDK complete.</p> + +<p class="paging-links"> +<a href="{@docRoot}sdk/installing/adding-packages.html" class="next-page-link"> +Continue: Adding SDK Packages</a></p> + + +</div> +<!-- ################ END STUDIO ##################### --> + + + + + + + + + +<!-- ################ JUST SDK TOOLS ##################### --> +<div id="tools" heading="Installing the Stand-alone SDK Tools" style="display:none"> + + +<p>The stand-alone SDK Tools package does not include a complete Android development environment. +It includes only the core SDK tools, which you can access from a command line or with a plugin +for your favorite IDE (if available).</p> + +<p>If you didn't download the SDK tools, go <a href="{@docRoot}sdk/index.html" +><b>download the SDK now</b></a>, +or switch to the <a href="{@docRoot}sdk/installing/index.html?pkg=adt">Eclipse ADT +install</a> or <a href="{@docRoot}sdk/installing/index.html?pkg=studio">Android Studio +install</a> instructions.</p> + + +<div class="procedure-box"> +<p id="instructions-toggle" +style="float:right;font-size:13px"><a href='' onclick='showAll();return false;' +>[ Show instructions for all platforms ]</a></p> + +<div class="win docs" style="display:none"> + +<p><b>To get started on Windows:</b></p> + <p>Your download package is an executable file that starts an installer. The installer checks your machine for required tools, such as the proper Java SE Development Kit (JDK) and installs it if necessary. The installer then saves the Android SDK Tools into a default location (or you can specify the location).</p> @@ -21,15 +215,9 @@ use to download the rest of the SDK packages (such as the latest system image).< <ol> <li>Double-click the executable ({@code .exe} file) to start the install.</li> <li>Make a note of the name and location in which it saves the SDK on your system—you will need to -refer to the SDK directory later, when setting up the ADT plugin and when using +refer to the SDK directory later when using the SDK tools from the command line.</li> -<li>Once the installation completes, the installer offers to start the Android SDK Manager. -If you'll be using Eclipse, <strong>do not</strong> start the Android SDK Manager, -and instead move on to <a href="{@docRoot}sdk/installing/installing-adt.html" ->Installing the Eclipse Plugin</a>. -<p>If you're using a different IDE, -start the SDK Manager and read <a href="{@docRoot}sdk/installing/adding-packages.html" ->Adding Platforms and Packages</a>.</p> +<li>Once the installation completes, the installer starts the Android SDK Manager. </li> </ol> @@ -37,51 +225,37 @@ start the SDK Manager and read <a href="{@docRoot}sdk/installing/adding-packages -<div id="mac" class="docs" style="display:none"> +<div class="mac docs" style="display:none"> -<h3>Getting started on Mac</h3> +<p><b>To get started on Mac OSX:</b></p> -<ol> -<li>Unpack the ZIP file you've downloaded. By default, it's unpacked +<p>Unpack the ZIP file you've downloaded. By default, it's unpacked into a directory named <code>android-sdk-mac_x86</code>. Move it to an appropriate location on your machine, -such as a "Development" directory in your home directory. +such as a "Development" directory in your home directory.</p> <p>Make a note of the name and location of the SDK directory on your system—you will need to -refer to the SDK directory later, when setting up the ADT plugin and when using +refer to the SDK directory later when using the SDK tools from the command line.</p> -</li> -<li>If you're using Eclipse, move on to <a href="{@docRoot}sdk/installing/installing-adt.html" ->Installing the Eclipse Plugin</a>. Otherwise, if you're using a different IDE, -read <a href="{@docRoot}sdk/installing/adding-packages.html" ->Adding Platforms and Packages</a>.</li> -</ol> </div> -<div id="linux" class="docs" style="display:none"> +<div class="linux docs" style="display:none"> -<h3>Getting started on Linux</h3> +<p><b>To get started on Linux:</b></p> -<ol> -<li>Unpack the {@code .tgz} file you've downloaded. By default, the SDK files are unpacked +<p>Unpack the {@code .tgz} file you've downloaded. By default, the SDK files are unpacked into a directory named <code>android-sdk-linux_x86</code>. Move it to an appropriate location on your machine, -such as a "Development" directory in your home directory. +such as a "Development" directory in your home directory.</p> <p>Make a note of the name and location of the SDK directory on your system—you will need to -refer to the SDK directory later, when setting up the ADT plugin and when using +refer to the SDK directory later when using the SDK tools from the command line.</p> -</li> -<li>If you're using Eclipse, move on to <a href="{@docRoot}sdk/installing/installing-adt.html" ->Installing the Eclipse Plugin</a>. Otherwise, if you're using a different IDE, -read <a href="{@docRoot}sdk/installing/adding-packages.html" ->Adding Platforms and Packages</a>.</li> -</ol> -<h5 id="Troubleshooting"><a href='' class="expandable" +<h5 id="Troubleshooting" style="margin-bottom:15px"><a href='' class="expandable" onclick="toggleExpandable(this,'#ubuntu-trouble');return false;" >Troubleshooting Ubuntu</a></h5> @@ -122,38 +296,129 @@ sudo apt-get install libncurses5:i386 libstdc++6:i386 zlib1g:i386</pre> </div><!-- end ubuntu trouble --> +</div><!-- end linux --> +</div><!-- end procedure box --> + + +<p>The Android SDK tools are now ready to begin developing apps, but there are still a +couple packages you should add to make your Android SDK complete.</p> + +<p class="paging-links"> +<a href="{@docRoot}sdk/installing/adding-packages.html" class="next-page-link"> +Continue: Adding SDK Packages</a></p> + + </div> +<!-- ################ END JUST TOOLS ##################### --> + + + + + +<!-- ################ DEFAULT ##################### --> +<style> +h3.large-link { + display:inline-block; + width:100%; + text-align:center; + border:1px solid #258aaf; + padding:10px 0; + color:inherit; +} +</style> + +<div id="default" style="display:none"> + +<p>If you haven't already done so, <b><a href="{@docRoot}sdk/index.html">click here to download +the Android SDK</a></b>. </p> + +<p>Otherwise, select which SDK package you want to install:</p> + +<div class="cols" style="margin-bottom:60px"> +<div class="col-4"> +<a href="{@docRoot}sdk/installing/index.html?pkg=adt"> +<h3 class="large-link">Eclipse ADT</h3> +</a> +</div> + +<div class="col-4"> +<a href="{@docRoot}sdk/installing/index.html?pkg=studio"> +<h3 class="large-link">Android Studio</h3></a> +</div> + +<div class="col-4"> +<a href="{@docRoot}sdk/installing/index.html?pkg=tools"> +<h3 class="large-link">Stand-alone SDK Tools</h3></a> +</div> +</div> + + +</div> +<!-- ################ END DEFAULT ##################### --> + + + + -<p style="margin-top:2em;"><a href='' onclick='showAll();return false;'>Information for other platforms</a></p> <script> - var $osDocs; - if (navigator.appVersion.indexOf("Win")!=-1) { - $osDocs = $('#win'); - } else if (navigator.appVersion.indexOf("Mac")!=-1) { - $osDocs = $('#mac'); - } else if (navigator.appVersion.indexOf("Linux")!=-1) { - $osDocs = $('#linux'); - } - if ($osDocs.length) { - // reveal only the docs for this OS - $osDocs.show(); - } else { - // not running a compatible OS, so just show all the docs - $('.docs').show(); +// Show proper instructions based on downloaded SDK package +var package = getUrlParam("pkg"); +if (package == "tools") { + // Show the SDK Tools (other IDE) instructions + $("h1").text($("#tools").attr('heading')); + $("#tools").show(); +} else if (package == "adt") { + // Show the ADT instructions + $("h1").text($("#adt").attr('heading')); + $("#adt").show(); +} else if (package == "studio") { + // Show the Android Studio instructions + $("h1").text($("#studio").attr('heading')); + $("#studio").show(); +} else { + // Show the default page content so user can select their setup + $("#default").show(); +} + +// Show the proper instructions based on machine OS +var $osDocs; +if (navigator.appVersion.indexOf("Win")!=-1) { + $osDocs = $('.win'); +} else if (navigator.appVersion.indexOf("Mac")!=-1) { + $osDocs = $('.mac'); +} else if (navigator.appVersion.indexOf("Linux")!=-1) { + $osDocs = $('.linux'); +} + +if ($osDocs.length) { + // reveal only the docs for this OS + $osDocs.show(); +} else { + // not running a compatible OS, so just show all the docs + $('.docs').show(); +} + + +/* Shows all the machine OS instructions */ +function showAll() { + $('.docs').show(); + $("#instructions-toggle").hide(); +} + +/* Returns the value for the given URL parameter */ +function getUrlParam(param) { + var url = window.location.search.substring(1); + var variables = url.split('&'); + for (var i = 0; i < variables.length; i++) { + var paramName = variables[i].split('='); + if (escape(paramName[0]) == param) { + return escape(paramName[1]); + } } +} + + - function showAll() { - $('.docs').each(function() { - if (!$(this).is(':visible')) { - console.log('show') - $(this).show(); - } else { - console.log('hide') - $(this).hide(); - $osDocs.show(); - } - }); - } </script> diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd index 7bf366c..b23212d 100644 --- a/docs/html/sdk/installing/installing-adt.jd +++ b/docs/html/sdk/installing/installing-adt.jd @@ -15,19 +15,16 @@ of Eclipse to let you quickly set up new Android projects, build an app UI, debug your app, and export signed (or unsigned) app packages (APKs) for distribution. </p> -<p>If you need to install Eclipse, you can download it from <a href= -"http://www.eclipse.org/downloads/">eclipse.org/downloads/</a>.</p> - - -<p class="note"><strong>Note:</strong> If you prefer to work in a different IDE, you do not need to -install Eclipse or ADT. Instead, you can directly use the SDK tools to build and -debug your application.</p> +<p class="note"><strong>Note:</strong> You should install the ADT plugin +only if you already have an Eclipse installation that you want to continue using. If you do not +have Eclipse installed, you should instead <b><a href="{@docRoot}sdk/index.html">install +the complete Android SDK</a></b>, which includes the latest IDE for Android developers.</p> <h2 id="Download">Download the ADT Plugin</h2> - +<p>To add the ADT plugin to Eclipse:</p> <ol> <li>Start Eclipse, then select <strong>Help</strong> > <strong>Install New Software</strong>.</li> diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd index 1b47f7f..7127ba6 100644 --- a/docs/html/sdk/installing/studio.jd +++ b/docs/html/sdk/installing/studio.jd @@ -7,7 +7,7 @@ page.tags=studio <div style="position:relative;min-height:660px;"> -<h3 style="color:#f80">EARLY ACCESS PREVIEW</h3> +<h3 style="color:#f80">BETA RELEASE</h3> <div id="tos" style="position:absolute;display:none;width:inherit;"> <div class="col-13" style="margin:0;"> </div><!-- provides top margin for content --> @@ -183,8 +183,6 @@ This is the Android Software Development Kit License Agreement - - <div id="main"> <div class="figure" style="width:400px;margin-top:-20px"> @@ -298,7 +296,7 @@ version by installing a patch. From within Android Studio, select Check for updates</strong>) to see whether an update is available.</p> <p>If an update is not available, -follow the <a href="#Installing">installation instructions</a> below and replace your existing +click the button above to download and replace your existing installation.</p> <div class="caution"> @@ -311,100 +309,6 @@ If you fail to copy these packages, then you can instead download them again thr the Android SDK Manager.</p> </div> - -<h2 id="Installing">Installing Android Studio</h2> -<p>Android Studio requires JDK 6 or greater (JRE alone is not sufficient). To check if you -have JDK installed (and which version), open a terminal and type <code>javac -version</code>. -If JDK is not available or the version is lower than 6, -<a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">download -JDK from here</a>.</p> -<p>To install Android Studio:</p> -<ol> -<li>Download the <strong>Android Studio</strong> package from above.</li> -<li>Install Android Studio and the SDK tools: - <p><b>Windows:</b></p> - <ol> - <li>Launch the downloaded EXE file, {@code android-studio-bundle-<version>.exe}.</li> - <li>Follow the setup wizard to install Android Studio. - - <div class="caution"><p><strong>Known issue:</strong> - On some Windows systems, the launcher script does not find where Java is installed. - If you encounter this problem, - you need to set an environment variable indicating the correct location.</p> - <p>Select <strong>Start menu > Computer > System Properties > - Advanced System Properties</strong>. Then open <strong>Advanced tab > Environment - Variables</strong> and add a new system variable <code>JAVA_HOME</code> that points to - your JDK folder, for example <code>C:\Program Files\Java\jdk1.7.0_21</code>.</p> - </div> - </li> - - </ol> - <p><b>Mac OS X:</b></p> - <ol> - <li>Open the downloaded DMG file, {@code android-studio-bundle-<version>.dmg}.</li> - <li>Drag and drop Android Studio into the Applications folder. - - <div class="caution"><p><strong>Known issue:</strong> - Depending on your security settings, when you attempt to open Android Studio, you might - see a warning that says the package is damaged and should be moved to the trash. If this - happens, go to <strong>System Preferences > Security & Privacy</strong> and under - <strong>Allow applications downloaded from</strong>, select <strong>Anywhere</strong>. - Then open Android Studio again.</p> - </div> - </li> - - </ol> - <p><b>Linux:</b></p> - <ol> - <li>Unpack the downloaded Tar file, {@code android-studio-bundle-<version>.tgz}, into an appropriate - location for your applications. - <li>To launch Android Studio, navigate to the {@code android-studio/bin/} directory - in a terminal and execute {@code studio.sh}. - <p>You may want to add {@code android-studio/bin/} to your PATH environmental - variable so that you can start Android Studio from any directory.</p> - </li> - </ol> -</li> -</ol> - -<p>That's it! You're ready to start developing apps with Android Studio.</p> - -<div class="note"> -<p><strong>Note:</strong> On Windows and Mac, the individual tools and -other SDK packages are saved within the Android Studio application directory. -To access the tools directly, use a terminal to navigate into the application and locate -the {@code sdk/} directory. For example:</p> -<p>Windows: <code>\Users\<user>\AppData\Local\Android\android-studio\sdk\</code></p> -<p>Mac: <code>/Applications/Android\ Studio.app/sdk/</code></p> -</div> - -<p>For a list of some known issues, see <a -href="http://tools.android.com/knownissues">tools.android.com/knownissues</a>.</p> - - -<h2 id="Start">Starting a Project</h2> - -<p>When you launch Android Studio for the first time, you'll see a Welcome -screen that offers several ways to get started:</p> - -<ul> - <li>To start building a new app, click <strong>New Project</strong>. - <p>This starts the New Project wizard, which helps you set up a project using an app template. - </li> - <li>To import an existing Android app project, click <strong>Import Project</strong>. - <p class="note"><strong>Note:</strong> If you previously developed your Android project - with Eclipse, you should first use the new export feature in the ADT plugin to prepare - your project with the new Gradle build system. For more information, read - <a href="{@docRoot}sdk/installing/migrate.html">Migrating from Eclipse</a> and - <a href="{@docRoot}sdk/installing/studio-build.html">Building Your Project with - Gradle</a>.</p> - </li> -</ul> - -<p>For additional help using Android Studio, read <a -href="{@docRoot}sdk/installing/studio-tips.html">Tips and Tricks</a>.</p> - - <p>As you continue developing apps, you may need to install additional versions of Android for the emulator and other packages such as the <a href="{@docRoot}tools/support-library/index.html">Android Support Library</a>. @@ -644,9 +548,13 @@ for possible resolutions to known issues: <a href="http://tools.android.com/know function onDownloadForRealz(link) { if ($("input#agree").is(':checked')) { - $("#tos").hide(); - $("#main").show(); - location.hash = "Updating"; + $("h1").text('Now downloading Android Studio...'); + $("#tos").slideUp(); + $("#jd-content .jd-descr").fadeOut('slow', function() { + setTimeout(function() { + window.location = "/sdk/installing/index.html?pkg=studio"; + }, 1000); + }); _gaq.push(['_trackEvent', 'SDK', 'Android Studio', $("#downloadForRealz").html()]); return true; } else { diff --git a/docs/html/tools/help/sdk-manager.jd b/docs/html/tools/help/sdk-manager.jd index 57271bb..b084237 100644 --- a/docs/html/tools/help/sdk-manager.jd +++ b/docs/html/tools/help/sdk-manager.jd @@ -3,7 +3,9 @@ page.title=SDK Manager <p>The Android SDK separates tools, platforms, and other components into packages you can - download using the SDK Manager.</p> + download using the SDK Manager. For example, when the SDK Tools are updated or a new version of +the Android platform is released, you can use the SDK Manager to quickly download them to +your environment.</p> <p>You can launch the SDK Manager in one of the following ways:</p> <ul> @@ -25,6 +27,14 @@ click <strong>Install</strong> to install the selected packages.</p> SDK packages that are available, already installed, or for which an update is available.</p> +<p>There are several different packages available for the Android SDK. The table below describes +most of the available packages and where they're located in your SDK directory +once you download them.</p> + + + + + <h2 id="Recommended">Recommended Packages</h2> <p>Here's an outline of the packages required and those we recommend you use: @@ -69,3 +79,77 @@ matches the API Level of the Android platform that you plan to use.</dd> <p class="note"><strong>Tip:</strong> For easy access to the SDK tools from a command line, add the location of the SDK's <code>tools/</code> and <code>platform-tools</code> to your <code>PATH</code> environment variable.</p> + + +<p>The above list is not comprehensive and you can <a +href="#AddingSites">add new sites</a> to download additional packages from third-parties.</p> + +<p>In some cases, an SDK package may require a specific minimum revision of +another package or SDK tool. +The development tools will notify you with warnings if there is dependency that you need to +address. The Android SDK Manager also enforces dependencies by requiring that you download any +packages that are needed by those you have selected.</p> + + + + + +<h2 id="AddingSites">Adding New Sites</h2> + +<p>By default, <strong>Available Packages</strong> displays packages available from the +<em>Android Repository</em> and <em>Third party Add-ons</em>. You can add other sites that host +their own Android SDK add-ons, then download the SDK add-ons +from those sites.</p> + +<p>For example, a mobile carrier or device manufacturer might offer additional +API libraries that are supported by their own Android-powered devices. In order +to develop using their libraries, you must install their Android SDK add-on, if it's not already +available under <em>Third party Add-ons</em>. </p> + +<p>If a carrier or device manufacturer has hosted an SDK add-on repository file +on their web site, follow these steps to add their site to the Android SDK +Manager:</p> + +<ol> + <li>Select <strong>Available Packages</strong> in the left panel.</li> + <li>Click <strong>Add Add-on Site</strong> and enter the URL of the +<code>repository.xml</code> file. Click <strong>OK</strong>.</li> +</ol> +<p>Any SDK packages available from the site will now be listed under a new item named +<strong>User Add-ons</strong>.</p> + + + + +<h2 id="troubleshooting">Troubleshooting</h2> + +<p><strong>Problems connecting to the SDK repository</strong></p> + +<p>If you are using the Android SDK Manager to download packages and are encountering +connection problems, try connecting over http, rather than https. To switch the +protocol used by the Android SDK Manager, follow these steps: </p> + +<ol> + <li>With the Android SDK Manager window open, select "Settings" in the + left pane. </li> + <li>On the right, in the "Misc" section, check the checkbox labeled "Force + https://... sources to be fetched using http://..." </li> + <li>Click <strong>Save & Apply</strong>.</li> +</ol> + + + + + + + + + + + + + + + + + diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs index b29b87c..450a5c4 100644 --- a/docs/html/tools/tools_toc.cs +++ b/docs/html/tools/tools_toc.cs @@ -10,44 +10,32 @@ <div class="nav-section-header"><a href="<?cs var:toroot ?>sdk/index.html"><span class="en">Download</span></a></div> <ul> - <li><a href="<?cs var:toroot ?>sdk/installing/bundle.html"> - <span class="en">Setting Up the ADT Bundle</span></a></li> + <li><a href="<?cs var:toroot ?>sdk/installing/index.html"> + <span class="en">Installing the SDK</span></a></li> - <li class="nav-section"> - <div class="nav-section-header"> - <a href="<?cs var:toroot ?>sdk/installing/index.html"><span class="en">Setting Up - an Existing IDE</span></a></div> - <ul> - <li><a href="<?cs var:toroot ?>sdk/installing/installing-adt.html"> - <span class="en">Installing the Eclipse Plugin</span></a></li> - <li><a href="<?cs var:toroot ?>sdk/installing/adding-packages.html"> - <span class="en">Adding Platforms and Packages</span></a></li> - </ul> - </li> - - <li class="nav-section"> - <div class="nav-section-header"> - <a href="<?cs var:toroot ?>sdk/installing/studio.html">Android Studio</a> - </div> - <ul> - <li><a href="<?cs var:toroot ?>sdk/installing/migrate.html"> - Migrating from Eclipse</a></li> - <li><a href="<?cs var:toroot ?>sdk/installing/studio-tips.html"> - Tips and Tricks</a></li> - <li><a href="<?cs var:toroot ?>sdk/installing/studio-layout.html"> - Using the Layout Editor</a></li> - <li><a href="<?cs var:toroot ?>sdk/installing/studio-build.html"> - Building Your Project with Gradle</a></li> - <li><a href="<?cs var:toroot ?>sdk/installing/studio-debug.html"> - Debugging with Android Studio</a></li> - </ul> - </li> - <li><a href="<?cs var:toroot ?>sdk/exploring.html"> - <span class="en">Exploring the SDK</span></a></li> - <li><a href="<?cs var:toroot ?>tools/sdk/ndk/index.html">Download the NDK</a> - </li> + <li><a href="<?cs var:toroot ?>sdk/installing/adding-packages.html"> + <span class="en">Adding SDK Packages</span></a></li> </ul> </li> + + + <li class="nav-section"> + <div class="nav-section-header"> + <a href="<?cs var:toroot ?>sdk/installing/studio.html">Android Studio</a> + </div> + <ul> + <li><a href="<?cs var:toroot ?>sdk/installing/migrate.html"> + Migrating from Eclipse</a></li> + <li><a href="<?cs var:toroot ?>sdk/installing/studio-tips.html"> + Tips and Tricks</a></li> + <li><a href="<?cs var:toroot ?>sdk/installing/studio-layout.html"> + Using the Layout Editor</a></li> + <li><a href="<?cs var:toroot ?>sdk/installing/studio-build.html"> + Building Your Project with Gradle</a></li> + <li><a href="<?cs var:toroot ?>sdk/installing/studio-debug.html"> + Debugging with Android Studio</a></li> + </ul> + </li> <li class="nav-section"> <div class="nav-section-header"> @@ -174,7 +162,13 @@ class="en">Support Library</span></a></div> class="en">Tools Help</span></a></div> <ul> <li><a href="<?cs var:toroot ?>tools/help/adb.html">adb</a></li> - <li><a href="<?cs var:toroot ?>tools/help/adt.html">ADT</a></li> + <li class="nav-section"> + <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/help/adt.html">ADT</a></div> + <ul> + <li><a href="<?cs var:toroot ?>sdk/installing/installing-adt.html"> + <span class="en">Installing the Eclipse Plugin</span></a></li> + </ul> + </li> <li><a href="<?cs var:toroot ?>tools/help/android.html">android</a></li> <li><a href="<?cs var:toroot ?>tools/help/avd-manager.html">AVD Manager</a></li> <li><a href="<?cs var:toroot ?>tools/help/bmgr.html">bmgr</a> @@ -244,6 +238,11 @@ class="en">Platforms</span></a></li> </ul> </li> + <li class="nav-section"> + <div class="nav-section-header empty"> + <a href="<?cs var:toroot ?>tools/sdk/ndk/index.html">NDK</a> + </div> + </li> <li class="nav-section"> <div class="nav-section-header"> diff --git a/docs/html/training/articles/keystore.jd b/docs/html/training/articles/keystore.jd new file mode 100644 index 0000000..bbbda67 --- /dev/null +++ b/docs/html/training/articles/keystore.jd @@ -0,0 +1,107 @@ +page.title=Android Keystore System +@jd:body + +<div id="qv-wrapper"> + <div id="qv"> + <h2>In this document</h2> + <ol> + <li><a href="#WhichShouldIUse">Choosing Between a Keychain or the Android Keystore Provider</a></li> + <li><a href="#UsingAndroidKeyStore">Using Android Keystore Provider + </a></li> + <ol> + <li><a href="#GeneratingANewPrivateKey">Generating a New Private Key</a></li> + <li><a href="#WorkingWithKeyStoreEntries">Working with Keystore Entries</a></li> + <li><a href="#ListingEntries">Listing Entries</a></li> + <li><a href="#SigningAndVerifyingData">Signing and Verifying Data</a></li> + </ol> + </ol> + + <h2>Blog articles</h2> + <ol> + <li><a + href="http://android-developers.blogspot.com/2012/03/unifying-key-store-access-in-ics.html"> + <h4>Unifying Key Store Access in ICS</h4> + </a></li> + </ol> + </div> +</div> + +<p>The Android Keystore system lets you store private keys + in a container to make it more difficult to extract from the + device. Once keys are in the keystore, they can be used for + cryptographic operations with the private key material remaining + non-exportable.</p> + +<p>The Keystore system is used by the {@link + android.security.KeyChain} API as well as the Android + Keystore provider feature that was introduced in Android 4.3 + (API level 18). This document goes over when and how to use the + Android Keystore provider.</p> + +<h2 id="WhichShouldIUse">Choosing Between a Keychain or the +Android Keystore Provider</h2> + +<p>Use the {@link android.security.KeyChain} API when you want + system-wide credentials. When an app requests the use of any credential + through the {@link android.security.KeyChain} API, users get to + choose, through a system-provided UI, which of the installed credentials + an app can access. This allows several apps to use the + same set of credentials with user consent.</p> + +<p>Use the Android Keystore provider to let an individual app store its own + credentials that only the app itself can access. + This provides a way for apps to manage credentials that are usable + only by itself while providing the same security benefits that the + {@link android.security.KeyChain} API provides for system-wide + credentials. This method requires no user interaction to select the credentials.</p> + +<h2 id="UsingAndroidKeyStore">Using Android Keystore Provider</h2> + +<p> +To use this feature, you use the standard {@link java.security.KeyStore} +and {@link java.security.KeyPairGenerator} classes along with the +{@code AndroidKeyStore} provider introduced in Android 4.3 (API level 18).</p> + +<p>{@code AndroidKeyStore} is registered as a {@link + java.security.KeyStore} type for use with the {@link + java.security.KeyStore#getInstance(String) KeyStore.getInstance(type)} + method and as a provider for use with the {@link + java.security.KeyPairGenerator#getInstance(String, String) + KeyPairGenerator.getInstance(algorithm, provider)} method.</p> + +<h3 id="GeneratingANewPrivateKey">Generating a New Private Key</h3> + +<p>Generating a new {@link java.security.PrivateKey} requires that + you also specify the initial X.509 attributes that the self-signed + certificate will have. You can replace the certificate at a later + time with a certificate signed by a Certificate Authority.</p> + +<p>To generate the key, use a {@link java.security.KeyPairGenerator} + with {@link android.security.KeyPairGeneratorSpec}:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java generate} + +<h3 id="WorkingWithKeyStoreEntries">Working with Keystore Entries</h3> + +<p>Using the {@code AndroidKeyStore} provider takes place through + all the standard {@link java.security.KeyStore} APIs.</p> + +<h4 id="ListingEntries">Listing Entries</h4> + +<p>List entries in the keystore by calling the {@link + java.security.KeyStore#aliases()} method:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java list} + +<h4 id="SigningAndVerifyingData">Signing and Verifying Data</h4> + +<p>Sign data by fetching the {@link + java.security.KeyStore.Entry} from the keystore and using the + {@link java.security.Signature} APIs, such as {@link + java.security.Signature#sign()}:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java sign} + +<p>Similarly, verify data with the {@link java.security.Signature#verify(byte[])} method:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java verify} diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 06cf253..ef0a411 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -194,6 +194,11 @@ public final class Bitmap implements Parcelable { * while {@link #getAllocationByteCount()} will reflect that of the initial * configuration.</p> * + * <p>Note: This may change this result of hasAlpha(). When converting to 565, + * the new bitmap will always be considered opaque. When converting from 565, + * the new bitmap will be considered non-opaque, and will respect the value + * set by setPremultiplied().</p> + * * <p>WARNING: This method should NOT be called on a bitmap currently used * by the view system. It does not make guarantees about how the underlying * pixel buffer is remapped to the new config, just that the allocation is @@ -217,7 +222,8 @@ public final class Bitmap implements Parcelable { throw new IllegalStateException("native-backed bitmaps may not be reconfigured"); } - nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length); + nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length, + mIsPremultiplied); mWidth = width; mHeight = height; } @@ -1586,7 +1592,8 @@ public final class Bitmap implements Parcelable { private static native void nativeDestructor(long nativeBitmap); private static native boolean nativeRecycle(long nativeBitmap); private static native void nativeReconfigure(long nativeBitmap, int width, int height, - int config, int allocSize); + int config, int allocSize, + boolean isPremultiplied); private static native boolean nativeCompress(long nativeBitmap, int format, int quality, OutputStream stream, diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java index de458af..a021165 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -53,7 +53,7 @@ public class Picture { public Picture(Picture src) { this(nativeConstructor(src != null ? src.mNativePicture : 0), false); } - + /** * To record a picture, call beginRecording() and then draw into the Canvas * that is returned. Nothing we appear on screen, but all of the draw @@ -67,7 +67,7 @@ public class Picture { mRecordingCanvas = new RecordingCanvas(this, ni); return mRecordingCanvas; } - + /** * Call endRecording when the picture is built. After this call, the picture * may be drawn, but the canvas that was returned by beginRecording must not @@ -92,16 +92,19 @@ public class Picture { * does not reflect (per se) the content of the picture. */ public native int getHeight(); - + /** - * Draw this picture on the canvas. The picture may have the side effect - * of changing the matrix and clip of the canvas. - * + * Draw this picture on the canvas. + * <p> + * Prior to {@link android.os.Build.VERSION_CODES#L}, this call could + * have the side effect of changing the matrix and clip of the canvas + * if this picture had imbalanced saves/restores. + * * <p> * <strong>Note:</strong> This forces the picture to internally call * {@link Picture#endRecording()} in order to prepare for playback. * - * @param canvas The picture is drawn to this canvas + * @param canvas The picture is drawn to this canvas */ public void draw(Canvas canvas) { if (mRecordingCanvas != null) { @@ -119,7 +122,7 @@ public class Picture { * <p> * <strong>Note:</strong> a picture created from an input stream cannot be * replayed on a hardware accelerated canvas. - * + * * @see #writeToStream(java.io.OutputStream) * @deprecated The recommended alternative is to not use writeToStream and * instead draw the picture into a Bitmap from which you can persist it as @@ -167,7 +170,7 @@ public class Picture { final long ni() { return mNativePicture; } - + private Picture(long nativePicture, boolean fromStream) { if (nativePicture == 0) { throw new RuntimeException(); @@ -187,7 +190,7 @@ public class Picture { private static native boolean nativeWriteToStream(long nativePicture, OutputStream stream, byte[] storage); private static native void nativeDestructor(long nativePicture); - + private static class RecordingCanvas extends Canvas { private final Picture mPicture; @@ -195,7 +198,7 @@ public class Picture { super(nativeCanvas); mPicture = pict; } - + @Override public void setBitmap(Bitmap bitmap) { throw new RuntimeException( diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java new file mode 100644 index 0000000..968c0ec --- /dev/null +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package android.graphics.drawable; + +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.Log; + +import com.android.internal.R; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * AnimatedVectorDrawable can use ObjectAnimator and AnimatorSet to animate + * the property of the VectorDrawable. + * + * @hide + */ +public class AnimatedVectorDrawable extends Drawable implements Animatable { + private static final String LOGTAG = AnimatedVectorDrawable.class.getSimpleName(); + + private static final String ANIMATED_VECTOR = "animated-vector"; + private static final String TARGET = "target"; + + private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false; + + private final AnimatedVectorDrawableState mAnimatedVectorState; + + + public AnimatedVectorDrawable() { + mAnimatedVectorState = new AnimatedVectorDrawableState( + new AnimatedVectorDrawableState(null)); + } + + private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res, + Theme theme) { + // TODO: Correctly handle the constant state for AVD. + mAnimatedVectorState = new AnimatedVectorDrawableState(state); + if (theme != null && canApplyTheme()) { + applyTheme(theme); + } + } + + @Override + public ConstantState getConstantState() { + return null; + } + + @Override + public void draw(Canvas canvas) { + mAnimatedVectorState.mVectorDrawable.draw(canvas); + if (isRunning()) { + invalidateSelf(); + } + } + + @Override + protected void onBoundsChange(Rect bounds) { + mAnimatedVectorState.mVectorDrawable.setBounds(bounds); + } + + @Override + public int getAlpha() { + return mAnimatedVectorState.mVectorDrawable.getAlpha(); + } + + @Override + public void setAlpha(int alpha) { + mAnimatedVectorState.mVectorDrawable.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + return mAnimatedVectorState.mVectorDrawable.getOpacity(); + } + + @Override + public int getIntrinsicWidth() { + return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight(); + } + + @Override + public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) + throws XmlPullParserException, IOException { + + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + final String tagName = parser.getName(); + if (ANIMATED_VECTOR.equals(tagName)) { + final TypedArray a = obtainAttributes(res, theme, attrs, + R.styleable.AnimatedVectorDrawable); + int drawableRes = a.getResourceId( + R.styleable.AnimatedVectorDrawable_drawable, 0); + if (drawableRes != 0) { + mAnimatedVectorState.mVectorDrawable = (VectorDrawable) res.getDrawable( + drawableRes); + } + a.recycle(); + } else if (TARGET.equals(tagName)) { + final TypedArray a = obtainAttributes(res, theme, attrs, + R.styleable.AnimatedVectorDrawableTarget); + final String target = a.getString( + R.styleable.AnimatedVectorDrawableTarget_name); + + int id = a.getResourceId( + R.styleable.AnimatedVectorDrawableTarget_animation, 0); + if (id != 0) { + Animator objectAnimator = AnimatorInflater.loadAnimator(res, theme, id); + setupAnimatorsForTarget(target, objectAnimator); + } + a.recycle(); + } + } + + eventType = parser.next(); + } + } + + @Override + public boolean canApplyTheme() { + return super.canApplyTheme() || mAnimatedVectorState != null + && mAnimatedVectorState.canApplyTheme(); + } + + @Override + public void applyTheme(Theme t) { + super.applyTheme(t); + + final VectorDrawable vectorDrawable = mAnimatedVectorState.mVectorDrawable; + if (vectorDrawable != null && vectorDrawable.canApplyTheme()) { + vectorDrawable.applyTheme(t); + } + } + + private static class AnimatedVectorDrawableState extends ConstantState { + int mChangingConfigurations; + VectorDrawable mVectorDrawable; + ArrayList<Animator> mAnimators; + + public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy) { + if (copy != null) { + mChangingConfigurations = copy.mChangingConfigurations; + // TODO: Make sure the constant state are handled correctly. + mVectorDrawable = new VectorDrawable(); + mAnimators = new ArrayList<Animator>(); + } + } + + @Override + public Drawable newDrawable() { + return new AnimatedVectorDrawable(this, null, null); + } + + @Override + public Drawable newDrawable(Resources res) { + return new AnimatedVectorDrawable(this, res, null); + } + + @Override + public Drawable newDrawable(Resources res, Theme theme) { + return new AnimatedVectorDrawable(this, res, theme); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + } + + private void setupAnimatorsForTarget(String name, Animator animator) { + Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name); + animator.setTarget(target); + mAnimatedVectorState.mAnimators.add(animator); + if (DBG_ANIMATION_VECTOR_DRAWABLE) { + Log.v(LOGTAG, "add animator for target " + name + " " + animator); + } + } + + @Override + public boolean isRunning() { + final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; + final int size = animators.size(); + for (int i = 0; i < size; i++) { + final Animator animator = animators.get(i); + if (animator.isRunning()) { + return true; + } + } + return false; + } + + @Override + public void start() { + final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; + final int size = animators.size(); + for (int i = 0; i < size; i++) { + final Animator animator = animators.get(i); + if (animator.isPaused()) { + animator.resume(); + } else if (!animator.isRunning()) { + animator.start(); + } + } + invalidateSelf(); + } + + @Override + public void stop() { + final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators; + final int size = animators.size(); + for (int i = 0; i < size; i++) { + final Animator animator = animators.get(i); + animator.pause(); + } + } +} diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java index 18e8e52..6a7757b 100644 --- a/graphics/java/android/graphics/drawable/Drawable.java +++ b/graphics/java/android/graphics/drawable/Drawable.java @@ -209,7 +209,7 @@ public abstract class Drawable { * stored bounds of this drawable. * * @see #copyBounds() - * @see #copyBounds(android.graphics.Rect) + * @see #copyBounds(android.graphics.Rect) */ public final Rect getBounds() { if (mBounds == ZERO_BOUNDS_RECT) { @@ -328,8 +328,8 @@ public abstract class Drawable { * that want to support animated drawables. * * @param cb The client's Callback implementation. - * - * @see #getCallback() + * + * @see #getCallback() */ public final void setCallback(Callback cb) { mCallback = new WeakReference<Callback>(cb); @@ -338,10 +338,10 @@ public abstract class Drawable { /** * Return the current {@link Callback} implementation attached to this * Drawable. - * + * * @return A {@link Callback} instance or null if no callback was set. - * - * @see #setCallback(android.graphics.drawable.Drawable.Callback) + * + * @see #setCallback(android.graphics.drawable.Drawable.Callback) */ public Callback getCallback() { if (mCallback != null) { @@ -349,15 +349,15 @@ public abstract class Drawable { } return null; } - + /** * Use the current {@link Callback} implementation to have this Drawable * redrawn. Does nothing if there is no Callback attached to the * Drawable. * * @see Callback#invalidateDrawable - * @see #getCallback() - * @see #setCallback(android.graphics.drawable.Drawable.Callback) + * @see #getCallback() + * @see #setCallback(android.graphics.drawable.Drawable.Callback) */ public void invalidateSelf() { final Callback callback = getCallback(); @@ -931,7 +931,7 @@ public abstract class Drawable { Rects only to drop them on the floor. */ Rect pad = new Rect(); - + // Special stuff for compatibility mode: if the target density is not // the same as the display density, but the resource -is- the same as // the display density, then don't scale it down to the target density. @@ -1040,6 +1040,8 @@ public abstract class Drawable { drawable = new GradientDrawable(); } else if (name.equals("vector")) { drawable = new VectorDrawable(); + } else if (name.equals("animated-vector")) { + drawable = new AnimatedVectorDrawable(); } else if (name.equals("scale")) { drawable = new ScaleDrawable(); } else if (name.equals("clip")) { @@ -1047,7 +1049,7 @@ public abstract class Drawable { } else if (name.equals("rotate")) { drawable = new RotateDrawable(); } else if (name.equals("animated-rotate")) { - drawable = new AnimatedRotateDrawable(); + drawable = new AnimatedRotateDrawable(); } else if (name.equals("animation-list")) { drawable = new AnimationDrawable(); } else if (name.equals("inset")) { diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java index 4623fa8..0d70e75 100644 --- a/graphics/java/android/graphics/drawable/Ripple.java +++ b/graphics/java/android/graphics/drawable/Ripple.java @@ -68,6 +68,8 @@ class Ripple { private float mStartingX; private float mStartingY; + private float mClampedStartingX; + private float mClampedStartingY; // Hardware rendering properties. private CanvasProperty<Paint> mPropPaint; @@ -112,6 +114,7 @@ class Ripple { public Ripple(RippleDrawable owner, Rect bounds, float startingX, float startingY) { mOwner = owner; mBounds = bounds; + mStartingX = startingX; mStartingY = startingY; } @@ -131,6 +134,23 @@ class Ripple { mOuterX = 0; mOuterY = 0; mDensity = density; + + clampStartingPosition(); + } + + private void clampStartingPosition() { + final float dX = mStartingX - mBounds.exactCenterX(); + final float dY = mStartingY - mBounds.exactCenterY(); + final float r = mOuterRadius; + if (dX * dX + dY * dY > r * r) { + // Point is outside the circle, clamp to the circumference. + final double angle = Math.atan2(dY, dX); + mClampedStartingX = (float) (Math.cos(angle) * r); + mClampedStartingY = (float) (Math.sin(angle) * r); + } else { + mClampedStartingX = mStartingX; + mClampedStartingY = mStartingY; + } } public void onHotspotBoundsChanged() { @@ -138,6 +158,8 @@ class Ripple { final float halfWidth = mBounds.width() / 2.0f; final float halfHeight = mBounds.height() / 2.0f; mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight); + + clampStartingPosition(); } } @@ -247,8 +269,10 @@ class Ripple { final int alpha = (int) (paintAlpha * mOpacity + 0.5f); final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius); if (alpha > 0 && radius > 0) { - final float x = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX); - final float y = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY); + final float x = MathUtils.lerp( + mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX); + final float y = MathUtils.lerp( + mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY); p.setAlpha(alpha); p.setStyle(Style.FILL); c.drawCircle(x, y, radius, p); @@ -277,6 +301,8 @@ class Ripple { public void move(float x, float y) { mStartingX = x; mStartingY = y; + + clampStartingPosition(); } /** @@ -361,8 +387,10 @@ class Ripple { int inflectionOpacity) { mPendingAnimations.clear(); - final float startX = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX); - final float startY = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY); + final float startX = MathUtils.lerp( + mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX); + final float startY = MathUtils.lerp( + mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY); final Paint outerPaint = new Paint(); outerPaint.setAntiAlias(true); outerPaint.setColor(mColor); diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index e37fbeb..0512ecc 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -48,22 +48,41 @@ import java.io.IOException; * A touch feedback drawable may contain multiple child layers, including a * special mask layer that is not drawn to the screen. A single layer may be set * as the mask by specifying its android:id value as {@link android.R.id#mask}. + * <pre> + * <code><!-- A red ripple masked against an opaque rectangle. --/> + * <ripple android:color="#ffff0000"> + * <item android:id="@android:id/mask" + * android:drawable="#ffffffff" /> + * <ripple /></code> + * </pre> * <p> * If a mask layer is set, the ripple effect will be masked against that layer - * before it is blended onto the composite of the remaining child layers. + * before it is drawn over the composite of the remaining child layers. * <p> - * If no mask layer is set, the ripple effect is simply blended onto the - * composite of the child layers using the specified - * {@link android.R.styleable#RippleDrawable_tintMode}. + * If no mask layer is set, the ripple effect is masked against the composite + * of the child layers. + * <pre> + * <code><!-- A blue ripple drawn atop a green rectangle. --/> + * <ripple android:color="#ff00ff00"> + * <item android:drawable="#ff0000ff" /> + * <ripple /> + * + * <!-- A red ripple drawn atop a drawable resource. --/> + * <ripple android:color="#ff00ff00"> + * <item android:drawable="@drawable/my_drawable" /> + * <ripple /></code> + * </pre> * <p> * If no child layers or mask is specified and the ripple is set as a View - * background, the ripple will be blended onto the first available parent - * background within the View's hierarchy using the specified - * {@link android.R.styleable#RippleDrawable_tintMode}. In this case, the - * drawing region may extend outside of the Drawable bounds. + * background, the ripple will be drawn atop the first available parent + * background within the View's hierarchy. In this case, the drawing region + * may extend outside of the Drawable bounds. + * <pre> + * <code><!-- An unbounded green ripple. --/> + * <ripple android:color="#ff0000ff" /></code> + * </pre> * - * @attr ref android.R.styleable#DrawableStates_state_focused - * @attr ref android.R.styleable#DrawableStates_state_pressed + * @attr ref android.R.styleable#RippleDrawable_color */ public class RippleDrawable extends LayerDrawable { private static final String LOG_TAG = RippleDrawable.class.getSimpleName(); @@ -188,8 +207,8 @@ public class RippleDrawable extends LayerDrawable { setActive(active && enabled); // Update the paint color. Only applicable when animated in software. - if (mRipplePaint != null && mState.mTint != null) { - final ColorStateList stateList = mState.mTint; + if (mRipplePaint != null && mState.mColor != null) { + final ColorStateList stateList = mState.mColor; final int newColor = stateList.getColorForState(stateSet, 0); final int oldColor = mRipplePaint.getColor(); if (oldColor != newColor) { @@ -248,10 +267,8 @@ public class RippleDrawable extends LayerDrawable { return true; } - @Override - public void setTint(ColorStateList tint, Mode tintMode) { - mState.mTint = tint; - mState.setTintMode(tintMode); + public void setColor(ColorStateList color) { + mState.mColor = color; invalidateSelf(); } @@ -312,22 +329,15 @@ public class RippleDrawable extends LayerDrawable { // Extract the theme attributes, if any. state.mTouchThemeAttrs = a.extractThemeAttrs(); - final ColorStateList tint = a.getColorStateList(R.styleable.RippleDrawable_tint); - if (tint != null) { - mState.mTint = tint; - } - - final int tintMode = a.getInt(R.styleable.RippleDrawable_tintMode, -1); - if (tintMode != -1) { - mState.setTintMode(Drawable.parseTintMode(tintMode, Mode.SRC_ATOP)); + final ColorStateList color = a.getColorStateList(R.styleable.RippleDrawable_color); + if (color != null) { + mState.mColor = color; } - mState.mPinned = a.getBoolean(R.styleable.RippleDrawable_pinned, mState.mPinned); - // If we're not waiting on a theme, verify required attributes. - if (state.mTouchThemeAttrs == null && mState.mTint == null) { + if (state.mTouchThemeAttrs == null && mState.mColor == null) { throw new XmlPullParserException(a.getPositionDescription() + - ": <ripple> requires a valid tint attribute"); + ": <ripple> requires a valid color attribute"); } } @@ -372,11 +382,6 @@ public class RippleDrawable extends LayerDrawable { @Override public void setHotspot(float x, float y) { - if (mState.mPinned && !circleContains(mHotspotBounds, x, y)) { - x = mHotspotBounds.exactCenterX(); - y = mHotspotBounds.exactCenterY(); - } - if (mHotspot == null) { mHotspot = new Ripple(this, mHotspotBounds, x, y); @@ -388,18 +393,6 @@ public class RippleDrawable extends LayerDrawable { } } - private boolean circleContains(Rect bounds, float x, float y) { - final float pX = bounds.exactCenterX() - x; - final float pY = bounds.exactCenterY() - y; - final double pointRadius = Math.sqrt(pX * pX + pY * pY); - - final float bX = bounds.width() / 2.0f; - final float bY = bounds.height() / 2.0f; - final double boundsRadius = Math.sqrt(bX * bX + bY * bY); - - return pointRadius < boundsRadius; - } - /** * Creates an active hotspot at the specified location. */ @@ -417,7 +410,7 @@ public class RippleDrawable extends LayerDrawable { mHotspot = new Ripple(this, mHotspotBounds, x, y); } - final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT); + final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT); mHotspot.setup(mState.mMaxRadius, color, mDensity); mHotspot.enter(); @@ -485,8 +478,10 @@ public class RippleDrawable extends LayerDrawable { final int contentLayer = drawNonMaskContent ? drawContentLayer(canvas, bounds, SRC_OVER) : -1; - // Next, try to draw the ripples (into a layer if necessary). - final int rippleLayer = drawRippleLayer(canvas, bounds, mState.mTintXfermode); + // Next, try to draw the ripples (into a layer if necessary). If we need + // to mask against the underlying content, set the xfermode to SRC_ATOP. + final PorterDuffXfermode xfermode = (hasMask || !drawNonMaskContent) ? SRC_OVER : SRC_ATOP; + final int rippleLayer = drawRippleLayer(canvas, bounds, xfermode); // If we have ripples and a non-opaque mask, draw the masking layer. if (rippleLayer >= 0 && drawMask) { @@ -533,14 +528,27 @@ public class RippleDrawable extends LayerDrawable { } private int drawContentLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) { - // TODO: We don't need a layer if all the content is opaque. + final ChildDrawable[] array = mLayerState.mChildren; + final int count = mLayerState.mNum; + + // We don't need a layer if we don't expect to draw any ripples, we have + // an explicit mask, or if the non-mask content is all opaque. + boolean needsLayer = false; + if (mAnimatingRipplesCount > 0 && mMask == null) { + for (int i = 0; i < count; i++) { + if (array[i].mId != R.id.mask + && array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) { + needsLayer = true; + break; + } + } + } + final Paint maskingPaint = getMaskingPaint(mode); - final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top, - bounds.right, bounds.bottom, maskingPaint); + final int restoreToCount = needsLayer ? canvas.saveLayer(bounds.left, bounds.top, + bounds.right, bounds.bottom, maskingPaint) : -1; // Draw everything except the mask. - final ChildDrawable[] array = mLayerState.mChildren; - final int count = mLayerState.mNum; for (int i = 0; i < count; i++) { if (array[i].mId != R.id.mask) { array[i].mDrawable.draw(canvas); @@ -559,8 +567,8 @@ public class RippleDrawable extends LayerDrawable { // Separate the ripple color and alpha channel. The alpha will be // applied when we merge the ripples down to the canvas. final int rippleARGB; - if (mState.mTint != null) { - rippleARGB = mState.mTint.getColorForState(getState(), Color.TRANSPARENT); + if (mState.mColor != null) { + rippleARGB = mState.mColor.getColorForState(getState(), Color.TRANSPARENT); } else { rippleARGB = Color.TRANSPARENT; } @@ -665,31 +673,19 @@ public class RippleDrawable extends LayerDrawable { static class RippleState extends LayerState { int[] mTouchThemeAttrs; - ColorStateList mTint = null; - PorterDuffXfermode mTintXfermode = SRC_ATOP; + ColorStateList mColor = null; int mMaxRadius = RADIUS_AUTO; - boolean mPinned = false; public RippleState(RippleState orig, RippleDrawable owner, Resources res) { super(orig, owner, res); if (orig != null) { mTouchThemeAttrs = orig.mTouchThemeAttrs; - mTint = orig.mTint; - mTintXfermode = orig.mTintXfermode; + mColor = orig.mColor; mMaxRadius = orig.mMaxRadius; - mPinned = orig.mPinned; } } - public void setTintMode(Mode mode) { - mTintXfermode = new PorterDuffXfermode(mode); - } - - public PorterDuffXfermode getTintXfermode() { - return mTintXfermode; - } - @Override public boolean canApplyTheme() { return mTouchThemeAttrs != null || super.canApplyTheme(); diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index c6c5b31..c531c22 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -27,6 +27,7 @@ import android.graphics.PathMeasure; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; +import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; @@ -40,7 +41,6 @@ import org.xmlpull.v1.XmlPullParserFactory; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.Stack; /** @@ -135,6 +135,8 @@ public class VectorDrawable extends Drawable { private final VectorDrawableState mVectorState; + private final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>(); + public VectorDrawable() { mVectorState = new VectorDrawableState(null); } @@ -147,6 +149,10 @@ public class VectorDrawable extends Drawable { } } + Object getTargetByName(String name) { + return mVGTargetsMap.get(name); + } + @Override public ConstantState getConstantState() { return mVectorState; @@ -162,6 +168,11 @@ public class VectorDrawable extends Drawable { } @Override + public int getAlpha() { + return mVectorState.mVPathRenderer.getRootAlpha(); + } + + @Override public void setAlpha(int alpha) { if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) { mVectorState.mVPathRenderer.setRootAlpha(alpha); @@ -302,6 +313,9 @@ public class VectorDrawable extends Drawable { final VPath path = new VPath(); path.inflate(res, attrs, theme); currentGroup.add(path); + if (path.getPathName() != null) { + mVGTargetsMap.put(path.getPathName(), path); + } noPathTag = false; } else if (SHAPE_SIZE.equals(tagName)) { pathRenderer.parseSize(res, attrs); @@ -314,6 +328,9 @@ public class VectorDrawable extends Drawable { newChildGroup.inflate(res, attrs, theme); currentGroup.mChildGroupList.add(newChildGroup); groupStack.push(newChildGroup); + if (newChildGroup.getGroupName() != null) { + mVGTargetsMap.put(newChildGroup.getGroupName(), newChildGroup); + } noGroupTag = false; } } else if (eventType == XmlPullParser.END_TAG) { @@ -363,7 +380,7 @@ public class VectorDrawable extends Drawable { indent += " "; } // Print the current node - Log.v(LOGTAG, indent + "current group is :" + currentGroup.getName() + Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName() + " rotation is " + currentGroup.mRotate); Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString()); // Then print all the children @@ -672,8 +689,7 @@ public class VectorDrawable extends Drawable { } - private static class VGroup { - private final HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>(); + static class VGroup { private final ArrayList<VPath> mPathList = new ArrayList<VPath>(); private final ArrayList<VGroup> mChildGroupList = new ArrayList<VGroup>(); @@ -694,10 +710,98 @@ public class VectorDrawable extends Drawable { private int[] mThemeAttrs; - private String mName = null; + private String mGroupName = null; + + /* Getter and Setter */ + public float getRotation() { + return mRotate; + } + + public void setRotation(float rotation) { + if (rotation != mRotate) { + mRotate = rotation; + updateLocalMatrix(); + } + } + + public float getPivotX() { + return mPivotX; + } + + public void setPivotX(float pivotX) { + if (pivotX != mPivotX) { + mPivotX = pivotX; + updateLocalMatrix(); + } + } + + public float getPivotY() { + return mPivotY; + } + + public void setPivotY(float pivotY) { + if (pivotY != mPivotY) { + mPivotY = pivotY; + updateLocalMatrix(); + } + } + + public float getScaleX() { + return mScaleX; + } - public String getName() { - return mName; + public void setScaleX(float scaleX) { + if (scaleX != mScaleX) { + mScaleX = scaleX; + updateLocalMatrix(); + } + } + + public float getScaleY() { + return mScaleY; + } + + public void setScaleY(float scaleY) { + if (scaleY != mScaleY) { + mScaleY = scaleY; + updateLocalMatrix(); + } + } + + public float getTranslateX() { + return mTranslateX; + } + + public void setTranslateX(float translateX) { + if (translateX != mTranslateX) { + mTranslateX = translateX; + updateLocalMatrix(); + } + } + + public float getTranslateY() { + return mTranslateY; + } + + public void setTranslateY(float translateY) { + if (translateY != mTranslateY) { + mTranslateY = translateY; + updateLocalMatrix(); + } + } + + public float getAlpha() { + return mGroupAlpha; + } + + public void setAlpha(float groupAlpha) { + if (groupAlpha != mGroupAlpha) { + mGroupAlpha = groupAlpha; + } + } + + public String getGroupName() { + return mGroupName; } public Matrix getLocalMatrix() { @@ -705,8 +809,6 @@ public class VectorDrawable extends Drawable { } public void add(VPath path) { - String id = path.getID(); - mVGPathMap.put(id, path); mPathList.add(path); } @@ -732,7 +834,7 @@ public class VectorDrawable extends Drawable { mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha); updateLocalMatrix(); if (a.hasValue(R.styleable.VectorDrawableGroup_name)) { - mName = a.getString(R.styleable.VectorDrawableGroup_name); + mGroupName = a.getString(R.styleable.VectorDrawableGroup_name); } a.recycle(); } @@ -774,7 +876,7 @@ public class VectorDrawable extends Drawable { } if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_name] == 0) { - mName = a.getString(R.styleable.VectorDrawableGroup_name); + mGroupName = a.getString(R.styleable.VectorDrawableGroup_name); } if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_alpha] == 0) { @@ -805,19 +907,15 @@ public class VectorDrawable extends Drawable { } - private static class VPath { - private static final int MAX_STATES = 10; - + static class VPath { private int[] mThemeAttrs; int mStrokeColor = 0; float mStrokeWidth = 0; float mStrokeOpacity = Float.NaN; - int mFillColor = Color.BLACK; int mFillRule; float mFillOpacity = Float.NaN; - float mTrimPathStart = 0; float mTrimPathEnd = 1; float mTrimPathOffset = 0; @@ -828,7 +926,7 @@ public class VectorDrawable extends Drawable { float mStrokeMiterlimit = 4; private VNode[] mNode = null; - private String mId; + private String mPathName; public VPath() { // Empty constructor. @@ -841,8 +939,8 @@ public class VectorDrawable extends Drawable { } } - public String getID() { - return mId; + public String getPathName() { + return mPathName; } private Paint.Cap getStrokeLineCap(int id, Paint.Cap defValue) { @@ -871,6 +969,71 @@ public class VectorDrawable extends Drawable { } } + /* Setters and Getters */ + int getStroke() { + return mStrokeColor; + } + + void setStroke(int strokeColor) { + mStrokeColor = strokeColor; + } + + float getStrokeWidth() { + return mStrokeWidth; + } + + void setStrokeWidth(float strokeWidth) { + mStrokeWidth = strokeWidth; + } + + float getStrokeOpacity() { + return mStrokeOpacity; + } + + void setStrokeOpacity(float strokeOpacity) { + mStrokeOpacity = strokeOpacity; + } + + int getFill() { + return mFillColor; + } + + void setFill(int fillColor) { + mFillColor = fillColor; + } + + float getFillOpacity() { + return mFillOpacity; + } + + void setFillOpacity(float fillOpacity) { + mFillOpacity = fillOpacity; + } + + float getTrimPathStart() { + return mTrimPathStart; + } + + void setTrimPathStart(float trimPathStart) { + mTrimPathStart = trimPathStart; + } + + float getTrimPathEnd() { + return mTrimPathEnd; + } + + void setTrimPathEnd(float trimPathEnd) { + mTrimPathEnd = trimPathEnd; + } + + float getTrimPathOffset() { + return mTrimPathOffset; + } + + void setTrimPathOffset(float trimPathOffset) { + mTrimPathOffset = trimPathOffset; + } + public void inflate(Resources r, AttributeSet attrs, Theme theme) { final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath); final int[] themeAttrs = a.extractThemeAttrs(); @@ -883,7 +1046,7 @@ public class VectorDrawable extends Drawable { } if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_name] == 0) { - mId = a.getString(R.styleable.VectorDrawablePath_name); + mPathName = a.getString(R.styleable.VectorDrawablePath_name); } if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pathData] == 0) { @@ -966,7 +1129,7 @@ public class VectorDrawable extends Drawable { mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip); if (a.hasValue(R.styleable.VectorDrawablePath_name)) { - mId = a.getString(R.styleable.VectorDrawablePath_name); + mPathName = a.getString(R.styleable.VectorDrawablePath_name); } if (a.hasValue(R.styleable.VectorDrawablePath_pathData)) { diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index 21d6caa..4a823cc 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -35,7 +35,7 @@ import javax.security.auth.x500.X500Principal; /** * This provides the required parameters needed for initializing the * {@code KeyPairGenerator} that works with - * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore + * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore * facility</a>. The Android KeyStore facility is accessed through a * {@link java.security.KeyPairGenerator} API using the {@code AndroidKeyStore} * provider. The {@code context} passed in may be used to pop up some UI to ask @@ -306,7 +306,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * Builder class for {@link KeyPairGeneratorSpec} objects. * <p> * This will build a parameter spec for use with the <a href="{@docRoot} - * guide/topics/security/keystore.html">Android KeyStore facility</a>. + * training/articles/keystore.html">Android KeyStore facility</a>. * <p> * The required fields must be filled in with the builder. * <p> diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java index b71efc4..2eeb6ad 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -24,7 +24,7 @@ import java.security.KeyStore.ProtectionParameter; /** * This provides the optional parameters that can be specified for * {@code KeyStore} entries that work with - * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore + * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore * facility</a>. The Android KeyStore facility is accessed through a * {@link java.security.KeyStore} API using the {@code AndroidKeyStore} * provider. The {@code context} passed in may be used to pop up some UI to ask @@ -67,7 +67,7 @@ public final class KeyStoreParameter implements ProtectionParameter { * Builder class for {@link KeyStoreParameter} objects. * <p> * This will build protection parameters for use with the - * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore + * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore * facility</a>. * <p> * This can be used to require that KeyStore entries be stored encrypted. diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index 8aa8c92..898e81a 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -26,8 +26,23 @@ namespace android { namespace uirenderer { +NullDamageAccumulator NullDamageAccumulator::sInstance; + +NullDamageAccumulator* NullDamageAccumulator::instance() { + return &sInstance; +} + +enum TransformType { + TransformRenderNode, + TransformMatrix4, +}; + struct DirtyStack { - const RenderNode* node; + TransformType type; + union { + const RenderNode* renderNode; + const Matrix4* matrix4; + }; // When this frame is pop'd, this rect is mapped through the above transform // and applied to the previous (aka parent) frame SkRect pendingDirty; @@ -42,7 +57,7 @@ DamageAccumulator::DamageAccumulator() { mHead->prev = mHead; } -void DamageAccumulator::pushNode(const RenderNode* node) { +void DamageAccumulator::pushCommon() { if (!mHead->next) { DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); nextFrame->next = 0; @@ -50,42 +65,120 @@ void DamageAccumulator::pushNode(const RenderNode* node) { mHead->next = nextFrame; } mHead = mHead->next; - mHead->node = node; mHead->pendingDirty.setEmpty(); } -void DamageAccumulator::popNode() { +void DamageAccumulator::pushTransform(const RenderNode* transform) { + pushCommon(); + mHead->type = TransformRenderNode; + mHead->renderNode = transform; +} + +void DamageAccumulator::pushTransform(const Matrix4* transform) { + pushCommon(); + mHead->type = TransformMatrix4; + mHead->matrix4 = transform; +} + +void DamageAccumulator::popTransform() { LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!"); DirtyStack* dirtyFrame = mHead; mHead = mHead->prev; - if (!dirtyFrame->pendingDirty.isEmpty()) { - SkRect mappedDirty; - const RenderProperties& props = dirtyFrame->node->properties(); - const SkMatrix* transform = props.getTransformMatrix(); - if (transform && !transform->isIdentity()) { - transform->mapRect(&mappedDirty, dirtyFrame->pendingDirty); - } else { - mappedDirty = dirtyFrame->pendingDirty; + if (dirtyFrame->type == TransformRenderNode) { + applyRenderNodeTransform(dirtyFrame); + } else { + applyMatrix4Transform(dirtyFrame); + } +} + +static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + Rect temp(in); + matrix->mapRect(temp); + out->join(RECT_ARGS(temp)); +} + +void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) { + mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty); +} + +static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + const SkMatrix* transform = props.getTransformMatrix(); + SkRect temp(in); + if (transform && !transform->isIdentity()) { + transform->mapRect(&temp); + } + temp.offset(props.getLeft(), props.getTop()); + out->join(temp); +} + +static DirtyStack* findParentRenderNode(DirtyStack* frame) { + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode) { + return frame; } - if (CC_LIKELY(mHead->node)) { - const RenderProperties& parentProps = mHead->node->properties(); - mappedDirty.offset(props.getLeft() - parentProps.getScrollX(), - props.getTop() - parentProps.getScrollY()); - if (props.getClipToBounds()) { - if (!mappedDirty.intersect(0, 0, parentProps.getWidth(), parentProps.getHeight())) { - mappedDirty.setEmpty(); - } - } - if (CC_UNLIKELY(!MathUtils::isZero(props.getTranslationZ()))) { - // TODO: Can we better bound the shadow damage area? For now - // match the old damageShadowReceiver() path and just dirty - // the entire parent bounds - mappedDirty.join(0, 0, parentProps.getWidth(), parentProps.getHeight()); + } + return NULL; +} + +static DirtyStack* findProjectionReceiver(DirtyStack* frame) { + if (frame) { + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode + && frame->renderNode->hasProjectionReceiver()) { + return frame; } + } + } + return NULL; +} + +static void applyTransforms(DirtyStack* frame, DirtyStack* end) { + SkRect* rect = &frame->pendingDirty; + while (frame != end) { + if (frame->type == TransformRenderNode) { + mapRect(frame->renderNode->properties(), *rect, rect); + } else { + mapRect(frame->matrix4, *rect, rect); + } + frame = frame->prev; + } +} + +void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { + if (frame->pendingDirty.isEmpty()) { + return; + } + + const RenderProperties& props = frame->renderNode->properties(); + + // Perform clipping + if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) { + if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) { + frame->pendingDirty.setEmpty(); + } + } + + // apply all transforms + mapRect(props, frame->pendingDirty, &mHead->pendingDirty); + + // project backwards if necessary + if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) { + // First, find our parent RenderNode: + DirtyStack* parentNode = findParentRenderNode(frame); + // Find our parent's projection receiver, which is what we project onto + DirtyStack* projectionReceiver = findProjectionReceiver(parentNode); + if (projectionReceiver) { + applyTransforms(frame, projectionReceiver); + projectionReceiver->pendingDirty.join(frame->pendingDirty); } else { - mappedDirty.offset(props.getLeft(), props.getTop()); + ALOGW("Failed to find projection receiver? Dropping on the floor..."); } - dirty(mappedDirty.fLeft, mappedDirty.fTop, mappedDirty.fRight, mappedDirty.fBottom); + + frame->pendingDirty.setEmpty(); } } diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index c62a351..2ca30d4 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -16,6 +16,7 @@ #ifndef DAMAGEACCUMULATOR_H #define DAMAGEACCUMULATOR_H +#include <cutils/compiler.h> #include <utils/LinearAllocator.h> #include <SkMatrix.h> @@ -28,8 +29,19 @@ namespace uirenderer { struct DirtyStack; class RenderNode; +class Matrix4; -class DamageAccumulator { +class IDamageAccumulator { +public: + virtual void pushTransform(const RenderNode* transform) = 0; + virtual void pushTransform(const Matrix4* transform) = 0; + virtual void popTransform() = 0; + virtual void dirty(float left, float top, float right, float bottom) = 0; +protected: + virtual ~IDamageAccumulator() {} +}; + +class DamageAccumulator : public IDamageAccumulator { PREVENT_COPY_AND_ASSIGN(DamageAccumulator); public: DamageAccumulator(); @@ -37,20 +49,44 @@ public: // Push a transform node onto the stack. This should be called prior // to any dirty() calls. Subsequent calls to dirty() - // will be affected by the node's transform when popNode() is called. - void pushNode(const RenderNode* node); + // will be affected by the transform when popTransform() is called. + virtual void pushTransform(const RenderNode* transform); + virtual void pushTransform(const Matrix4* transform); + // Pops a transform node from the stack, propagating the dirty rect - // up to the parent node. - void popNode(); - void dirty(float left, float top, float right, float bottom); + // up to the parent node. Returns the IDamageTransform that was just applied + virtual void popTransform(); + + virtual void dirty(float left, float top, float right, float bottom); void finish(SkRect* totalDirty); private: + void pushCommon(); + void applyMatrix4Transform(DirtyStack* frame); + void applyRenderNodeTransform(DirtyStack* frame); + LinearAllocator mAllocator; DirtyStack* mHead; }; +class NullDamageAccumulator : public IDamageAccumulator { + PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator); +public: + virtual void pushTransform(const RenderNode* transform) { } + virtual void pushTransform(const Matrix4* transform) { } + virtual void popTransform() { } + virtual void dirty(float left, float top, float right, float bottom) { } + + ANDROID_API static NullDamageAccumulator* instance(); + +private: + NullDamageAccumulator() {} + ~NullDamageAccumulator() {} + + static NullDamageAccumulator sInstance; +}; + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index 2268386..9f2014f 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -417,6 +417,8 @@ void Matrix4::mapPoint(float& x, float& y) const { } void Matrix4::mapRect(Rect& r) const { + if (isIdentity()) return; + if (isSimple()) { MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]); MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index e33a001..1c5c578 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -147,6 +147,7 @@ public: data[kTranslateX] += x; data[kTranslateY] += y; data[kTranslateZ] += z; + mType |= kTypeUnknown; } else { // Doing a translation will only affect the translate bit of the type // Save the type diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index c9921ba..59e15e1 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -975,11 +975,14 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo // Bezier approximation /////////////////////////////////////////////////////////////////////////////// +// Depth at which recursion is aborted +#define ABORT_DEPTH 20 + void PathTessellator::recursiveCubicBezierVertices( float p1x, float p1y, float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, - Vector<Vertex>& outputVertices) { + Vector<Vertex>& outputVertices, int depth) { float dx = p2x - p1x; float dy = p2y - p1y; float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); @@ -988,7 +991,7 @@ void PathTessellator::recursiveCubicBezierVertices( // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors - if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, p2x, p2y); } else { @@ -1012,11 +1015,11 @@ void PathTessellator::recursiveCubicBezierVertices( recursiveCubicBezierVertices( p1x, p1y, p1c1x, p1c1y, mx, my, p1c1c2x, p1c1c2y, - sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); recursiveCubicBezierVertices( mx, my, p2c1c2x, p2c1c2y, p2x, p2y, p2c2x, p2c2y, - sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); } } @@ -1025,12 +1028,12 @@ void PathTessellator::recursiveQuadraticBezierVertices( float bx, float by, float cx, float cy, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, - Vector<Vertex>& outputVertices) { + Vector<Vertex>& outputVertices, int depth) { float dx = bx - ax; float dy = by - ay; float d = (cx - bx) * dy - (cy - by) * dx; - if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, bx, by); } else { @@ -1044,9 +1047,9 @@ void PathTessellator::recursiveQuadraticBezierVertices( float my = (acy + bcy) * 0.5f; recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, - sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, - sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); + sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); } } diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h index f033470..8ac9a3b 100644 --- a/libs/hwui/PathTessellator.h +++ b/libs/hwui/PathTessellator.h @@ -105,7 +105,7 @@ private: float bx, float by, float cx, float cy, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, - Vector<Vertex> &outputVertices); + Vector<Vertex> &outputVertices, int depth = 0); /* endpoints p1, p2 @@ -117,7 +117,7 @@ private: float p2x, float p2y, float c2x, float c2y, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, - Vector<Vertex> &outputVertices); + Vector<Vertex> &outputVertices, int depth = 0); }; }; // namespace uirenderer diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index c2f6df8..83ad76f 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -15,6 +15,7 @@ */ #define ATRACE_TAG ATRACE_TAG_VIEW +#define LOG_TAG "RenderNode" #include "RenderNode.h" @@ -111,26 +112,20 @@ void RenderNode::prepareTree(TreeInfo& info) { prepareTreeImpl(info); } -static inline void pushNode(RenderNode* self, TreeInfo& info) { - if (info.damageAccumulator) { - info.damageAccumulator->pushNode(self); - } -} - -static inline void popNode(TreeInfo& info) { - if (info.damageAccumulator) { - info.damageAccumulator->popNode(); - } -} - void RenderNode::damageSelf(TreeInfo& info) { - if (info.damageAccumulator && isRenderable() && properties().getAlpha() > 0) { - info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); + if (isRenderable() && properties().getAlpha() > 0) { + if (properties().getClipToBounds()) { + info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); + } else { + // Hope this is big enough? + // TODO: Get this from the display list ops or something + info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX); + } } } void RenderNode::prepareTreeImpl(TreeInfo& info) { - pushNode(this, info); + info.damageAccumulator->pushTransform(this); if (info.mode == TreeInfo::MODE_FULL) { pushStagingChanges(info); evaluateAnimations(info); @@ -140,7 +135,7 @@ void RenderNode::prepareTreeImpl(TreeInfo& info) { evaluateAnimations(info); } prepareSubTree(info, mDisplayListData); - popNode(info); + info.damageAccumulator->popTransform(); } class PushAnimatorsFunctor { @@ -173,14 +168,14 @@ void RenderNode::pushStagingChanges(TreeInfo& info) { if (mDirtyPropertyFields) { mDirtyPropertyFields = 0; damageSelf(info); - popNode(info); + info.damageAccumulator->popTransform(); mProperties = mStagingProperties; - pushNode(this, info); // We could try to be clever and only re-damage if the matrix changed. // However, we don't need to worry about that. The cost of over-damaging // here is only going to be a single additional map rect of this node // plus a rect join(). The parent's transform (and up) will only be // performed once. + info.damageAccumulator->pushTransform(this); damageSelf(info); } if (mNeedsDisplayListDataSync) { @@ -217,7 +212,7 @@ void RenderNode::evaluateAnimations(TreeInfo& info) { // property push and just damage self before and after animators are run damageSelf(info); - popNode(info); + info.damageAccumulator->popTransform(); AnimateFunctor functor(this, info); std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd; @@ -226,7 +221,7 @@ void RenderNode::evaluateAnimations(TreeInfo& info) { mProperties.updateMatrix(); info.out.hasAnimations |= mAnimators.size(); - pushNode(this, info); + info.damageAccumulator->pushTransform(this); damageSelf(info); } @@ -243,8 +238,11 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]); } for (size_t i = 0; i < subtree->children().size(); i++) { - RenderNode* childNode = subtree->children()[i]->mDisplayList; + DrawDisplayListOp* op = subtree->children()[i]; + RenderNode* childNode = op->mDisplayList; + info.damageAccumulator->pushTransform(&op->mTransformFromParent); childNode->prepareTreeImpl(info); + info.damageAccumulator->popTransform(); } } } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 393d4ea..f0f6e7c 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -39,6 +39,7 @@ #include <androidfw/ResourceTypes.h> +#include "DamageAccumulator.h" #include "Debug.h" #include "Matrix.h" #include "DeferredDisplayList.h" @@ -125,6 +126,10 @@ public: return mDisplayListData && mDisplayListData->hasDrawOps; } + bool hasProjectionReceiver() const { + return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0; + } + const char* getName() const { return mName.string(); } diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 6163df5..5f7d4e3 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -44,7 +44,6 @@ RenderProperties::PrimitiveFields::PrimitiveFields() , mPivotX(0), mPivotY(0) , mLeft(0), mTop(0), mRight(0), mBottom(0) , mWidth(0), mHeight(0) - , mScrollX(0), mScrollY(0) , mPivotExplicitlySet(false) , mMatrixOrPivotDirty(false) , mCaching(false) { diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index c294f38..b012fc5 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -366,22 +366,6 @@ public: return false; } - bool setScrollX(int scrollX) { - return RP_SET(mPrimitiveFields.mScrollX, scrollX); - } - - bool setScrollY(int scrollY) { - return RP_SET(mPrimitiveFields.mScrollY, scrollY); - } - - int getScrollX() const { - return mPrimitiveFields.mScrollX; - } - - int getScrollY() const { - return mPrimitiveFields.mScrollY; - } - bool setCaching(bool caching) { return RP_SET(mPrimitiveFields.mCaching, caching); } @@ -481,7 +465,6 @@ private: float mPivotX, mPivotY; int mLeft, mTop, mRight, mBottom; int mWidth, mHeight; - int mScrollX, mScrollY; bool mPivotExplicitlySet; bool mMatrixOrPivotDirty; bool mCaching; diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 41cc9d2..ef3d0d7 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -39,6 +39,7 @@ namespace uirenderer { TessellationCache::Description::Description() : type(kNone) + , aa(false) , cap(SkPaint::kDefault_Cap) , style(SkPaint::kFill_Style) , strokeWidth(1.0f) { @@ -47,6 +48,7 @@ TessellationCache::Description::Description() TessellationCache::Description::Description(Type type) : type(type) + , aa(false) , cap(SkPaint::kDefault_Cap) , style(SkPaint::kFill_Style) , strokeWidth(1.0f) { @@ -55,6 +57,7 @@ TessellationCache::Description::Description(Type type) TessellationCache::Description::Description(Type type, const SkPaint* paint) : type(type) + , aa(paint->isAntiAlias()) , cap(paint->getStrokeCap()) , style(paint->getStyle()) , strokeWidth(paint->getStrokeWidth()) { @@ -63,6 +66,7 @@ TessellationCache::Description::Description(Type type, const SkPaint* paint) hash_t TessellationCache::Description::hash() const { uint32_t hash = JenkinsHashMix(0, type); + hash = JenkinsHashMix(hash, aa); hash = JenkinsHashMix(hash, cap); hash = JenkinsHashMix(hash, style); hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); @@ -165,12 +169,12 @@ public: ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque, const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ, const Vector3& lightCenter, float lightRadius) - : drawTransform(drawTransform) + : drawTransform(*drawTransform) , localClip(localClip) , opaque(opaque) - , casterPerimeter(casterPerimeter) - , transformXY(transformXY) - , transformZ(transformZ) + , casterPerimeter(*casterPerimeter) + , transformXY(*transformXY) + , transformZ(*transformZ) , lightCenter(lightCenter) , lightRadius(lightRadius) { } @@ -182,14 +186,19 @@ public: delete bufferPair; } - // Note - only the localClip is deep copied, since other pointers point at Allocator controlled - // objects, which are safe for the entire frame - const Matrix4* drawTransform; + /* Note - we deep copy all task parameters, because *even though* pointers into Allocator + * controlled objects (like the SkPath and Matrix4s) should be safe for the entire frame, + * certain Allocators are destroyed before trim() is called to flush incomplete tasks. + * + * These deep copies could be avoided, long term, by cancelling or flushing outstanding tasks + * before tearning down single-frame LinearAllocators. + */ + const Matrix4 drawTransform; const Rect localClip; bool opaque; - const SkPath* casterPerimeter; - const Matrix4* transformXY; - const Matrix4* transformZ; + const SkPath casterPerimeter; + const Matrix4 transformXY; + const Matrix4 transformZ; const Vector3 lightCenter; const float lightRadius; }; @@ -281,8 +290,8 @@ public: VertexBuffer* ambientBuffer = new VertexBuffer; VertexBuffer* spotBuffer = new VertexBuffer; - tessellateShadows(t->drawTransform, &t->localClip, t->opaque, t->casterPerimeter, - t->transformXY, t->transformZ, t->lightCenter, t->lightRadius, + tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter, + &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius, *ambientBuffer, *spotBuffer); t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer)); diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index 8f37230..d4ff943 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -55,6 +55,7 @@ public: }; Type type; + bool aa; SkPaint::Cap cap; SkPaint::Style style; float strokeWidth; diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 2096f98..fd78f8e 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -18,6 +18,7 @@ #include <utils/Timers.h> +#include "DamageAccumulator.h" #include "utils/Macros.h" namespace android { @@ -25,7 +26,6 @@ namespace uirenderer { class BaseRenderNodeAnimator; class AnimationListener; -class DamageAccumulator; class AnimationHook { public: @@ -62,7 +62,7 @@ public: , frameTimeMs(0) , animationHook(NULL) , prepareTextures(mode == MODE_FULL) - , damageAccumulator(0) + , damageAccumulator(NullDamageAccumulator::instance()) {} const TraversalMode mode; @@ -71,7 +71,8 @@ public: // TODO: Remove this? Currently this is used to signal to stop preparing // textures if we run out of cache space. bool prepareTextures; - DamageAccumulator* damageAccumulator; + // Must not be null + IDamageAccumulator* damageAccumulator; struct Out { Out() diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index ded10a1..4988f19 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -47,7 +47,7 @@ namespace renderthread { #define SETUP_TASK(method) \ LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \ - "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \ + "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \ METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \ MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index c3d5d94..6280fde 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1653,6 +1653,25 @@ public class AudioManager { } } + + /** + * Return a new audio session identifier not associated with any player or effect. + * It can for instance be used to create one of the {@link android.media.audiofx.AudioEffect} + * objects. + * @return a new unclaimed and unused audio session identifier, or {@link #ERROR} when the + * system failed to allocate a new session. + */ + public int allocateAudioSessionId() { + int session = AudioSystem.newAudioSessionId(); + if (session > 0) { + return session; + } else { + Log.e(TAG, "Failure to allocate a new audio session ID"); + return ERROR; + } + } + + /* * Sets a generic audio configuration parameter. The use of these parameters * are platform dependant, see libaudio @@ -2344,19 +2363,24 @@ public class AudioManager { if (rctlr == null) { return false; } - IAudioService service = getService(); - final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener(); - final ComponentName listenerComponent = new ComponentName(mContext, l.getClass()); - try { - int[] artworkDimensions = rctlr.getArtworkSize(); - boolean reg = service.registerRemoteController(rctlr.getRcDisplay(), - artworkDimensions[0]/*w*/, artworkDimensions[1]/*h*/, - listenerComponent); - rctlr.setIsRegistered(reg); - return reg; - } catch (RemoteException e) { - Log.e(TAG, "Dead object in registerRemoteController " + e); - return false; + if (USE_SESSIONS) { + rctlr.startListeningToSessions(); + return true; + } else { + IAudioService service = getService(); + final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener(); + final ComponentName listenerComponent = new ComponentName(mContext, l.getClass()); + try { + int[] artworkDimensions = rctlr.getArtworkSize(); + boolean reg = service.registerRemoteController(rctlr.getRcDisplay(), + artworkDimensions[0]/* w */, artworkDimensions[1]/* h */, + listenerComponent); + rctlr.setIsRegistered(reg); + return reg; + } catch (RemoteException e) { + Log.e(TAG, "Dead object in registerRemoteController " + e); + return false; + } } } @@ -2369,12 +2393,16 @@ public class AudioManager { if (rctlr == null) { return; } - IAudioService service = getService(); - try { - service.unregisterRemoteControlDisplay(rctlr.getRcDisplay()); - rctlr.setIsRegistered(false); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e); + if (USE_SESSIONS) { + rctlr.stopListeningToSessions(); + } else { + IAudioService service = getService(); + try { + service.unregisterRemoteControlDisplay(rctlr.getRcDisplay()); + rctlr.setIsRegistered(false); + } catch (RemoteException e) { + Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e); + } } } @@ -2998,7 +3026,8 @@ public class AudioManager { /** @hide */ public static final int SUCCESS = AudioSystem.SUCCESS; - /** @hide + /** + * A default error code. */ public static final int ERROR = AudioSystem.ERROR; /** @hide diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index c8d64ce..9fbcd18 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -130,6 +130,11 @@ public class AudioSystem public static native boolean isSourceActive(int source); /* + * Returns a new unused audio session ID + */ + public static native int newAudioSessionId(); + + /* * Sets a group generic audio configuration parameters. The use of these parameters * are platform dependent, see libaudio * diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java index 511111c..f9e49c1 100644 --- a/media/java/android/media/CamcorderProfile.java +++ b/media/java/android/media/CamcorderProfile.java @@ -90,9 +90,14 @@ public class CamcorderProfile */ public static final int QUALITY_QVGA = 7; + /** + * Quality level corresponding to the 2160p (3840x2160) resolution. + */ + public static final int QUALITY_2160P = 8; + // Start and end of quality list private static final int QUALITY_LIST_START = QUALITY_LOW; - private static final int QUALITY_LIST_END = QUALITY_QVGA; + private static final int QUALITY_LIST_END = QUALITY_2160P; /** * Time lapse quality level corresponding to the lowest available resolution. @@ -134,9 +139,14 @@ public class CamcorderProfile */ public static final int QUALITY_TIME_LAPSE_QVGA = 1007; + /** + * Time lapse quality level corresponding to the 2160p (3840 x 2160) resolution. + */ + public static final int QUALITY_TIME_LAPSE_2160P = 1008; + // Start and end of timelapse quality list private static final int QUALITY_TIME_LAPSE_LIST_START = QUALITY_TIME_LAPSE_LOW; - private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_QVGA; + private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2160P; /** * Default recording duration in seconds before the session is terminated. diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java index 3711585..76c7299 100644 --- a/media/java/android/media/RemoteController.java +++ b/media/java/android/media/RemoteController.java @@ -19,11 +19,17 @@ package android.media; import android.app.ActivityManager; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.media.IRemoteControlDisplay; import android.media.MediaMetadataEditor; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.MediaSessionLegacyHelper; +import android.media.session.MediaSessionManager; +import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -34,6 +40,7 @@ import android.util.Log; import android.view.KeyEvent; import java.lang.ref.WeakReference; +import java.util.List; /** * The RemoteController class is used to control media playback, display and update media metadata @@ -56,6 +63,7 @@ public final class RemoteController private final static int TRANSPORT_UNKNOWN = 0; private final static String TAG = "RemoteController"; private final static boolean DEBUG = false; + private final static boolean USE_SESSIONS = true; private final static Object mGenLock = new Object(); private final static Object mInfoLock = new Object(); private final RcDisplay mRcd; @@ -64,6 +72,11 @@ public final class RemoteController private final int mMaxBitmapDimension; private MetadataEditor mMetadataEditor; + private MediaSessionManager mSessionManager; + private MediaSessionManager.SessionListener mSessionListener + = new TopTransportSessionListener(); + private MediaController.Callback mSessionCb = new MediaControllerCallback(); + /** * Synchronized on mGenLock */ @@ -79,6 +92,8 @@ public final class RemoteController private int mArtworkWidth = -1; private int mArtworkHeight = -1; private boolean mEnabled = true; + // synchronized on mInfoLock, for USE_SESSION apis. + private MediaController mCurrentSession; /** * Class constructor. @@ -123,6 +138,8 @@ public final class RemoteController mContext = context; mRcd = new RcDisplay(this); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mSessionManager = (MediaSessionManager) context + .getSystemService(Context.MEDIA_SESSION_SERVICE); if (ActivityManager.isLowRamDeviceStatic()) { mMaxBitmapDimension = MAX_BITMAP_DIMENSION; @@ -194,8 +211,15 @@ public final class RemoteController * @hide */ public String getRemoteControlClientPackageName() { - return mClientPendingIntentCurrent != null ? - mClientPendingIntentCurrent.getCreatorPackage() : null; + if (USE_SESSIONS) { + synchronized (mInfoLock) { + return mCurrentSession != null ? mCurrentSession.getSessionInfo().getPackageName() + : null; + } + } else { + return mClientPendingIntentCurrent != null ? + mClientPendingIntentCurrent.getCreatorPackage() : null; + } } /** @@ -215,22 +239,38 @@ public final class RemoteController * @see OnClientUpdateListener#onClientPlaybackStateUpdate(int, long, long, float) */ public long getEstimatedMediaPosition() { - if (mLastPlaybackInfo != null) { - if (!RemoteControlClient.playbackPositionShouldMove(mLastPlaybackInfo.mState)) { - return mLastPlaybackInfo.mCurrentPosMs; + if (USE_SESSIONS) { + synchronized (mInfoLock) { + if (mCurrentSession != null) { + PlaybackState state = mCurrentSession.getPlaybackState(); + if (state != null) { + return state.getPosition(); + } + } } - - // Take the current position at the time of state change and estimate. - final long thenPos = mLastPlaybackInfo.mCurrentPosMs; - if (thenPos < 0) { - return -1; + } else { + final PlaybackInfo lastPlaybackInfo; + synchronized (mInfoLock) { + lastPlaybackInfo = mLastPlaybackInfo; } + if (lastPlaybackInfo != null) { + if (!RemoteControlClient.playbackPositionShouldMove(lastPlaybackInfo.mState)) { + return lastPlaybackInfo.mCurrentPosMs; + } + + // Take the current position at the time of state change and + // estimate. + final long thenPos = lastPlaybackInfo.mCurrentPosMs; + if (thenPos < 0) { + return -1; + } - final long now = SystemClock.elapsedRealtime(); - final long then = mLastPlaybackInfo.mStateChangeTimeMs; - final long sinceThen = now - then; - final long scaledSinceThen = (long) (sinceThen * mLastPlaybackInfo.mSpeed); - return thenPos + scaledSinceThen; + final long now = SystemClock.elapsedRealtime(); + final long then = lastPlaybackInfo.mStateChangeTimeMs; + final long sinceThen = now - then; + final long scaledSinceThen = (long) (sinceThen * lastPlaybackInfo.mSpeed); + return thenPos + scaledSinceThen; + } } return -1; } @@ -267,30 +307,40 @@ public final class RemoteController if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) { throw new IllegalArgumentException("not a media key event"); } - final PendingIntent pi; - synchronized(mInfoLock) { - if (!mIsRegistered) { - Log.e(TAG, "Cannot use sendMediaKeyEvent() from an unregistered RemoteController"); + if (USE_SESSIONS) { + synchronized (mInfoLock) { + if (mCurrentSession != null) { + return mCurrentSession.dispatchMediaButtonEvent(keyEvent); + } return false; } - if (!mEnabled) { - Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController"); - return false; + } else { + final PendingIntent pi; + synchronized (mInfoLock) { + if (!mIsRegistered) { + Log.e(TAG, + "Cannot use sendMediaKeyEvent() from an unregistered RemoteController"); + return false; + } + if (!mEnabled) { + Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController"); + return false; + } + pi = mClientPendingIntentCurrent; } - pi = mClientPendingIntentCurrent; - } - if (pi != null) { - Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); - intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); - try { - pi.send(mContext, 0, intent); - } catch (CanceledException e) { - Log.e(TAG, "Error sending intent for media button down: ", e); + if (pi != null) { + Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); + intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + try { + pi.send(mContext, 0, intent); + } catch (CanceledException e) { + Log.e(TAG, "Error sending intent for media button down: ", e); + return false; + } + } else { + Log.i(TAG, "No-op when sending key click, no receiver right now"); return false; } - } else { - Log.i(TAG, "No-op when sending key click, no receiver right now"); - return false; } return true; } @@ -311,11 +361,19 @@ public final class RemoteController if (timeMs < 0) { throw new IllegalArgumentException("illegal negative time value"); } - final int genId; - synchronized (mGenLock) { - genId = mClientGenerationIdCurrent; + if (USE_SESSIONS) { + synchronized (mInfoLock) { + if (mCurrentSession != null) { + mCurrentSession.getTransportControls().seekTo(timeMs); + } + } + } else { + final int genId; + synchronized (mGenLock) { + genId = mClientGenerationIdCurrent; + } + mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs); } - mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs); return true; } @@ -430,7 +488,6 @@ public final class RemoteController return editor; } - /** * A class to read the metadata published by a {@link RemoteControlClient}, or send a * {@link RemoteControlClient} new values for keys that can be edited. @@ -477,26 +534,41 @@ public final class RemoteController if (!mMetadataChanged) { return; } - final int genId; - synchronized(mGenLock) { - genId = mClientGenerationIdCurrent; - } - synchronized(mInfoLock) { - if (mEditorMetadata.containsKey( - String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) { - Rating rating = (Rating) getObject( - MediaMetadataEditor.RATING_KEY_BY_USER, null); - mAudioManager.updateRemoteControlClientMetadata(genId, - MediaMetadataEditor.RATING_KEY_BY_USER, - rating); - } else { - Log.e(TAG, "no metadata to apply"); + if (USE_SESSIONS) { + synchronized (mInfoLock) { + if (mCurrentSession != null) { + if (mEditorMetadata.containsKey( + String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) { + Rating rating = (Rating) getObject( + MediaMetadataEditor.RATING_KEY_BY_USER, null); + if (rating != null) { + mCurrentSession.getTransportControls().setRating(rating); + } + } + } + } + } else { + final int genId; + synchronized(mGenLock) { + genId = mClientGenerationIdCurrent; + } + synchronized(mInfoLock) { + if (mEditorMetadata.containsKey( + String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) { + Rating rating = (Rating) getObject( + MediaMetadataEditor.RATING_KEY_BY_USER, null); + mAudioManager.updateRemoteControlClientMetadata(genId, + MediaMetadataEditor.RATING_KEY_BY_USER, + rating); + } else { + Log.e(TAG, "no metadata to apply"); + } } - // NOT setting mApplied to true as this type of MetadataEditor will be applied - // multiple times, whenever the user of a RemoteController needs to change the - // metadata (e.g. user changes the rating of a song more than once during playback) - mApplied = false; } + // NOT setting mApplied to true as this type of MetadataEditor will be applied + // multiple times, whenever the user of a RemoteController needs to change the + // metadata (e.g. user changes the rating of a song more than once during playback) + mApplied = false; } } @@ -649,6 +721,46 @@ public final class RemoteController } } + /** + * This receives updates when the current session changes. This is + * registered to receive the updates on the handler thread so it can call + * directly into the appropriate methods. + */ + private class MediaControllerCallback extends MediaController.Callback { + @Override + public void onPlaybackStateChanged(PlaybackState state) { + onNewPlaybackState(state); + } + + @Override + public void onMetadataChanged(MediaMetadata metadata) { + onNewMediaMetadata(metadata); + } + } + + /** + * Listens for changes to the active session stack and replaces the + * currently tracked session if it has changed. + */ + private class TopTransportSessionListener extends MediaSessionManager.SessionListener { + @Override + public void onActiveSessionsChanged(List<MediaController> controllers) { + int size = controllers.size(); + for (int i = 0; i < size; i++) { + MediaController controller = controllers.get(i); + long flags = controller.getFlags(); + // We only care about sessions that handle transport controls, + // which will be true for apps using RCC + if ((flags & MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) { + updateController(controller); + return; + } + } + updateController(null); + } + + } + //================================================== // Event handling private final EventHandler mEventHandler; @@ -658,6 +770,8 @@ public final class RemoteController private final static int MSG_NEW_METADATA = 3; // msg always has non-null obj parameter private final static int MSG_CLIENT_CHANGE = 4; private final static int MSG_DISPLAY_ENABLE = 5; + private final static int MSG_NEW_PLAYBACK_STATE = 6; + private final static int MSG_NEW_MEDIA_METADATA = 7; private class EventHandler extends Handler { @@ -686,12 +800,46 @@ public final class RemoteController case MSG_DISPLAY_ENABLE: onDisplayEnable(msg.arg1 == 1); break; + case MSG_NEW_PLAYBACK_STATE: + // same as new playback info but using new apis + onNewPlaybackState((PlaybackState) msg.obj); + break; + case MSG_NEW_MEDIA_METADATA: + onNewMediaMetadata((MediaMetadata) msg.obj); + break; default: Log.e(TAG, "unknown event " + msg.what); } } } + /** + * @hide + */ + void startListeningToSessions() { + final ComponentName listenerComponent = new ComponentName(mContext, + mOnClientUpdateListener.getClass()); + mSessionManager.addActiveSessionsListener(mSessionListener, listenerComponent, + ActivityManager.getCurrentUser()); + mSessionListener.onActiveSessionsChanged(mSessionManager + .getActiveSessions(listenerComponent)); + if (DEBUG) { + Log.d(TAG, "Registered session listener with component " + listenerComponent + + " for user " + ActivityManager.getCurrentUser()); + } + } + + /** + * @hide + */ + void stopListeningToSessions() { + mSessionManager.removeActiveSessionsListener(mSessionListener); + if (DEBUG) { + Log.d(TAG, "Unregistered session listener for user " + + ActivityManager.getCurrentUser()); + } + } + /** If the msg is already queued, replace it with this one. */ private static final int SENDMSG_REPLACE = 0; /** If the msg is already queued, ignore this one and leave the old. */ @@ -713,6 +861,7 @@ public final class RemoteController handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delayMs); } + ///////////// These calls are used by the old APIs with RCC and RCD ////////////////////// private void onNewPendingIntent(int genId, PendingIntent pi) { synchronized(mGenLock) { if (mClientGenerationIdCurrent != genId) { @@ -848,6 +997,86 @@ public final class RemoteController } } + ///////////// These calls are used by the new APIs with Sessions ////////////////////// + private void updateController(MediaController controller) { + if (DEBUG) { + Log.d(TAG, "Updating controller to " + controller + " previous controller is " + + mCurrentSession); + } + synchronized (mInfoLock) { + if (controller == null) { + if (mCurrentSession != null) { + mCurrentSession.removeCallback(mSessionCb); + mCurrentSession = null; + sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE, + 0 /* genId */, 1 /* clearing */, null /* obj */, 0 /* delay */); + } + } else if (mCurrentSession == null + || !controller.getSessionInfo().getId() + .equals(mCurrentSession.getSessionInfo().getId())) { + if (mCurrentSession != null) { + mCurrentSession.removeCallback(mSessionCb); + } + sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE, + 0 /* genId */, 0 /* clearing */, null /* obj */, 0 /* delay */); + mCurrentSession = controller; + mCurrentSession.addCallback(mSessionCb, mEventHandler); + + PlaybackState state = controller.getPlaybackState(); + sendMsg(mEventHandler, MSG_NEW_PLAYBACK_STATE, SENDMSG_REPLACE, + 0 /* genId */, 0, state /* obj */, 0 /* delay */); + + MediaMetadata metadata = controller.getMetadata(); + sendMsg(mEventHandler, MSG_NEW_MEDIA_METADATA, SENDMSG_REPLACE, + 0 /* arg1 */, 0 /* arg2 */, metadata /* obj */, 0 /* delay */); + } + // else same controller, no need to update + } + } + + private void onNewPlaybackState(PlaybackState state) { + final OnClientUpdateListener l; + synchronized (mInfoLock) { + l = this.mOnClientUpdateListener; + } + if (l != null) { + int playstate = state == null ? RemoteControlClient.PLAYSTATE_NONE : PlaybackState + .getRccStateFromState(state.getState()); + if (state == null || state.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN) { + l.onClientPlaybackStateUpdate(playstate); + } else { + l.onClientPlaybackStateUpdate(playstate, state.getLastPositionUpdateTime(), + state.getPosition(), state.getPlaybackRate()); + } + if (state != null) { + l.onClientTransportControlUpdate(PlaybackState.getRccControlFlagsFromActions(state + .getActions())); + } + } + } + + private void onNewMediaMetadata(MediaMetadata metadata) { + if (metadata == null) { + // RemoteController only handles non-null metadata + return; + } + final OnClientUpdateListener l; + final MetadataEditor metadataEditor; + // prepare the received Bundle to be used inside a MetadataEditor + synchronized(mInfoLock) { + l = mOnClientUpdateListener; + boolean canRate = mCurrentSession != null + && mCurrentSession.getRatingType() != Rating.RATING_NONE; + long editableKeys = canRate ? MediaMetadataEditor.RATING_KEY_BY_USER : 0; + Bundle legacyMetadata = MediaSessionLegacyHelper.getOldMetadata(metadata); + mMetadataEditor = new MetadataEditor(legacyMetadata, editableKeys); + metadataEditor = mMetadataEditor; + } + if (l != null) { + l.onClientMetadataUpdate(metadataEditor); + } + } + //================================================== private static class PlaybackInfo { int mState; diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index 7c03907..f0cd785 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -37,6 +37,7 @@ interface ISessionController { boolean isTransportControlEnabled(); void showRoutePicker(); MediaSessionInfo getSessionInfo(); + long getFlags(); // These commands are for the TransportController void play(); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 57a0a54..5ca7daa 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -57,6 +57,7 @@ public final class MediaController { private final Object mLock = new Object(); private boolean mCbRegistered = false; + private MediaSessionInfo mInfo; private TransportControls mTransportController; @@ -174,6 +175,21 @@ public final class MediaController { } /** + * Get the flags for this session. + * + * @return The current set of flags for the session. + * @hide + */ + public long getFlags() { + try { + return mSessionBinder.getFlags(); + } catch (RemoteException e) { + Log.wtf(TAG, "Error calling getFlags.", e); + } + return 0; + } + + /** * Adds a callback to receive updates from the Session. Updates will be * posted on the caller's thread. * @@ -253,12 +269,14 @@ public final class MediaController { * @hide */ public MediaSessionInfo getSessionInfo() { - try { - return mSessionBinder.getSessionInfo(); - } catch (RemoteException e) { - Log.e(TAG, "Error in getSessionInfo.", e); + if (mInfo == null) { + try { + mInfo = mSessionBinder.getSessionInfo(); + } catch (RemoteException e) { + Log.e(TAG, "Error in getSessionInfo.", e); + } } - return null; + return mInfo; } /* diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java index 099f601..801844f 100644 --- a/media/java/android/media/session/MediaSessionLegacyHelper.java +++ b/media/java/android/media/session/MediaSessionLegacyHelper.java @@ -20,6 +20,10 @@ import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.Context; import android.content.Intent; +import android.media.MediaMetadata; +import android.media.MediaMetadataEditor; +import android.media.MediaMetadataRetriever; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.ArrayMap; @@ -64,6 +68,88 @@ public class MediaSessionLegacyHelper { return sInstance; } + public static Bundle getOldMetadata(MediaMetadata metadata) { + Bundle oldMetadata = new Bundle(); + if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ALBUM), + metadata.getString(MediaMetadata.METADATA_KEY_ALBUM)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_ART)) { + oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK), + metadata.getBitmap(MediaMetadata.METADATA_KEY_ART)); + } else if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART)) { + // Fall back to album art if the track art wasn't available + oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK), + metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ARTIST)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST), + metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ARTIST), + metadata.getString(MediaMetadata.METADATA_KEY_ARTIST)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_AUTHOR)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_AUTHOR), + metadata.getString(MediaMetadata.METADATA_KEY_AUTHOR)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_COMPILATION)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_COMPILATION), + metadata.getString(MediaMetadata.METADATA_KEY_COMPILATION)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_COMPOSER)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_COMPOSER), + metadata.getString(MediaMetadata.METADATA_KEY_COMPOSER)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_DATE)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DATE), + metadata.getString(MediaMetadata.METADATA_KEY_DATE)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_DISC_NUMBER)) { + oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER), + metadata.getLong(MediaMetadata.METADATA_KEY_DISC_NUMBER)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { + oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DURATION), + metadata.getLong(MediaMetadata.METADATA_KEY_DURATION)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_GENRE)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_GENRE), + metadata.getString(MediaMetadata.METADATA_KEY_GENRE)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) { + oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS), + metadata.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_RATING)) { + oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.RATING_KEY_BY_OTHERS), + metadata.getRating(MediaMetadata.METADATA_KEY_RATING)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_USER_RATING)) { + oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER), + metadata.getRating(MediaMetadata.METADATA_KEY_USER_RATING)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_TITLE), + metadata.getString(MediaMetadata.METADATA_KEY_TITLE)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) { + oldMetadata.putLong( + String.valueOf(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER), + metadata.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_WRITER)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_WRITER), + metadata.getString(MediaMetadata.METADATA_KEY_WRITER)); + } + if (metadata.containsKey(MediaMetadata.METADATA_KEY_YEAR)) { + oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_YEAR), + metadata.getString(MediaMetadata.METADATA_KEY_YEAR)); + } + return oldMetadata; + } + public MediaSession getSession(PendingIntent pi) { SessionHolder holder = mSessions.get(pi); return holder == null ? null : holder.mSession; diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 291bfc8..8eceee8 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -96,10 +96,13 @@ public final class MediaSessionManager { } /** - * Get a list of controllers for all ongoing sessions. This requires the - * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by - * the calling app. You may also retrieve this list if your app is an - * enabled notification listener using the + * Get a list of controllers for all ongoing sessions. The controllers will + * be provided in priority order with the most important controller at index + * 0. + * <p> + * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL + * permission be held by the calling app. You may also retrieve this list if + * your app is an enabled notification listener using the * {@link NotificationListenerService} APIs, in which case you must pass the * {@link ComponentName} of your enabled listener. * @@ -239,7 +242,8 @@ public final class MediaSessionManager { /** * Called when the list of active sessions has changed. This can be due * to a session being added or removed or the order of sessions - * changing. + * changing. The controllers will be provided in priority order with the + * most important controller at index 0. * * @param controllers The updated list of controllers for the user that * changed. diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java index e09ac3f..3b3f249 100644 --- a/media/java/android/media/session/PlaybackState.java +++ b/media/java/android/media/session/PlaybackState.java @@ -432,6 +432,8 @@ public final class PlaybackState implements Parcelable { return STATE_REWINDING; case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: return STATE_SKIPPING_TO_PREVIOUS; + case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: + return STATE_SKIPPING_TO_NEXT; case RemoteControlClient.PLAYSTATE_STOPPED: return STATE_STOPPED; default: @@ -440,6 +442,41 @@ public final class PlaybackState implements Parcelable { } /** + * Get the {@link RemoteControlClient} state for the given + * {@link PlaybackState} state. + * + * @param state The state used by {@link PlaybackState}. + * @return The equivalent state used by {@link RemoteControlClient}. + * @hide + */ + public static int getRccStateFromState(int state) { + switch (state) { + case STATE_BUFFERING: + return RemoteControlClient.PLAYSTATE_BUFFERING; + case STATE_ERROR: + return RemoteControlClient.PLAYSTATE_ERROR; + case STATE_FAST_FORWARDING: + return RemoteControlClient.PLAYSTATE_FAST_FORWARDING; + case STATE_NONE: + return RemoteControlClient.PLAYSTATE_NONE; + case STATE_PAUSED: + return RemoteControlClient.PLAYSTATE_PAUSED; + case STATE_PLAYING: + return RemoteControlClient.PLAYSTATE_PLAYING; + case STATE_REWINDING: + return RemoteControlClient.PLAYSTATE_REWINDING; + case STATE_SKIPPING_TO_PREVIOUS: + return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS; + case STATE_SKIPPING_TO_NEXT: + return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS; + case STATE_STOPPED: + return RemoteControlClient.PLAYSTATE_STOPPED; + default: + return -1; + } + } + + /** * @hide */ public static long getActionsFromRccControlFlags(int rccFlags) { @@ -454,6 +491,21 @@ public final class PlaybackState implements Parcelable { return actions; } + /** + * @hide + */ + public static int getRccControlFlagsFromActions(long actions) { + int rccFlags = 0; + long action = 1; + while (action <= actions && action < Integer.MAX_VALUE) { + if ((action & actions) != 0) { + rccFlags |= getRccFlagForAction(action); + } + action = action << 1; + } + return rccFlags; + } + private static long getActionForRccFlag(int flag) { switch (flag) { case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS: @@ -480,6 +532,35 @@ public final class PlaybackState implements Parcelable { return 0; } + private static int getRccFlagForAction(long action) { + // We only care about the lower set of actions that can map to rcc + // flags. + int testAction = action < Integer.MAX_VALUE ? (int) action : 0; + switch (testAction) { + case (int) ACTION_SKIP_TO_PREVIOUS: + return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS; + case (int) ACTION_REWIND: + return RemoteControlClient.FLAG_KEY_MEDIA_REWIND; + case (int) ACTION_PLAY: + return RemoteControlClient.FLAG_KEY_MEDIA_PLAY; + case (int) ACTION_PLAY_PAUSE: + return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE; + case (int) ACTION_PAUSE: + return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE; + case (int) ACTION_STOP: + return RemoteControlClient.FLAG_KEY_MEDIA_STOP; + case (int) ACTION_FAST_FORWARD: + return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD; + case (int) ACTION_SKIP_TO_NEXT: + return RemoteControlClient.FLAG_KEY_MEDIA_NEXT; + case (int) ACTION_SEEK_TO: + return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE; + case (int) ACTION_SET_RATING: + return RemoteControlClient.FLAG_KEY_MEDIA_RATING; + } + return 0; + } + public static final Parcelable.Creator<PlaybackState> CREATOR = new Parcelable.Creator<PlaybackState>() { @Override diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 52045d3..7ff2c2e 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -272,6 +272,15 @@ public final class TvContract { /** A generic channel type. */ public static final int TYPE_OTHER = 0x0; + /** The channel type for NTSC. */ + public static final int TYPE_NTSC = 0x1; + + /** The channel type for PAL. */ + public static final int TYPE_PAL = 0x2; + + /** The channel type for SECAM. */ + public static final int TYPE_SECAM = 0x3; + /** The special channel type used for pass-through inputs such as HDMI. */ public static final int TYPE_PASSTHROUGH = 0x00010000; diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 868c5bf..7b8f2ec 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -26,6 +26,7 @@ import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -223,6 +224,18 @@ public final class TvInputInfo implements Parcelable { return mService.loadLabel(pm); } + /** + * Loads the user-displayed icon for this TV input service. + * + * @param pm Supplies a PackageManager used to load the TV input's resources. + * @return a Drawable containing the TV input's icon. If the TV input does not have + * an icon, application icon is returned. If it's unavailable too, system default is + * returned. + */ + public Drawable loadIcon(PackageManager pm) { + return mService.serviceInfo.loadIcon(pm); + } + @Override public int describeContents() { return 0; diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 157dd49..fbe5340 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -24,6 +24,7 @@ #include <SkBitmap.h> #include <media/IMediaHTTPService.h> #include <media/mediametadataretriever.h> +#include <media/mediascanner.h> #include <private/media/VideoFrame.h> #include "jni.h" @@ -337,17 +338,13 @@ static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture( return NULL; } - unsigned int len = mediaAlbumArt->mSize; - char* data = (char*) mediaAlbumArt + sizeof(MediaAlbumArt); - jbyteArray array = env->NewByteArray(len); + jbyteArray array = env->NewByteArray(mediaAlbumArt->size()); if (!array) { // OutOfMemoryError exception has already been thrown. ALOGE("getEmbeddedPicture: OutOfMemoryError is thrown."); } else { - jbyte* bytes = env->GetByteArrayElements(array, NULL); - if (bytes != NULL) { - memcpy(bytes, data, len); - env->ReleaseByteArrayElements(array, bytes, 0); - } + const jbyte* data = + reinterpret_cast<const jbyte*>(mediaAlbumArt->data()); + env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data); } // No need to delete mediaAlbumArt here diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index b520440..321c2e3 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -348,17 +348,16 @@ android_media_MediaScanner_extractAlbumArt( } int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - MediaAlbumArt* mediaAlbumArt = - reinterpret_cast<MediaAlbumArt*>(mp->extractAlbumArt(fd)); + MediaAlbumArt* mediaAlbumArt = mp->extractAlbumArt(fd); if (mediaAlbumArt == NULL) { return NULL; } - jbyteArray array = env->NewByteArray(mediaAlbumArt->mSize); + jbyteArray array = env->NewByteArray(mediaAlbumArt->size()); if (array != NULL) { - jbyte* bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, &mediaAlbumArt->mData[0], mediaAlbumArt->mSize); - env->ReleaseByteArrayElements(array, bytes, 0); + const jbyte* data = + reinterpret_cast<const jbyte*>(mediaAlbumArt->data()); + env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data); } done: diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java index 3f6ced6..bc159cb 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java @@ -25,6 +25,7 @@ public class KeyguardSimpleHostView extends KeyguardViewBase { public KeyguardSimpleHostView(Context context, AttributeSet attrs) { super(context, attrs); + KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback); } @Override @@ -62,4 +63,10 @@ public class KeyguardSimpleHostView extends KeyguardViewBase { // TODO Auto-generated method stub } + private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onUserSwitchComplete(int userId) { + getSecurityContainer().showPrimarySecurityScreen(false /* turning off */); + } + }; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java index 7918755..a8b49e2 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java @@ -130,6 +130,11 @@ public class KeyguardStatusView extends GridLayout { } } + @Override + public boolean hasOverlappingRendering() { + return false; + } + private void updateOwnerInfo() { String ownerInfo = getOwnerInfo(); if (!TextUtils.isEmpty(ownerInfo)) { diff --git a/packages/PrintSpooler/res/drawable/print_button.xml b/packages/PrintSpooler/res/drawable/print_button.xml index d4b6a82..b59afba 100644 --- a/packages/PrintSpooler/res/drawable/print_button.xml +++ b/packages/PrintSpooler/res/drawable/print_button.xml @@ -16,8 +16,7 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="@color/print_button_tint_color" - android:pinned="true"> + android:color="@color/print_button_tint_color"> <item android:drawable="@drawable/print_button_background"> </item> diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index f71cafe..3e0d7e5 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -1735,10 +1735,12 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } else if (spinner == mOrientationSpinner) { SpinnerItem<Integer> orientationItem = mOrientationSpinnerAdapter.getItem(position); PrintAttributes attributes = mPrintJob.getAttributes(); - if (orientationItem.value == ORIENTATION_PORTRAIT) { - attributes.copyFrom(attributes.asPortrait()); - } else { - attributes.copyFrom(attributes.asLandscape()); + if (mMediaSizeSpinner.getSelectedItem() != null) { + if (orientationItem.value == ORIENTATION_PORTRAIT) { + attributes.copyFrom(attributes.asPortrait()); + } else { + attributes.copyFrom(attributes.asLandscape()); + } } } diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 0e025a9..a92ab7e 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -195,4 +195,7 @@ <!-- Default for Settings.Secure.WAKE_GESTURE_ENABLED --> <bool name="def_wake_gesture_enabled">true</bool> + <!-- Default for Settings.Global.GUEST_USER_ENABLED --> + <bool name="def_guest_user_enabled">true</bool> + </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 9b21ae4..09e6a94 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -70,7 +70,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 104; + private static final int DATABASE_VERSION = 105; private Context mContext; private int mUserHandle; @@ -1677,6 +1677,24 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 104; } + if (upgradeVersion < 105) { + if (mUserHandle == UserHandle.USER_OWNER) { + db.beginTransaction(); + SQLiteStatement stmt = null; + try { + stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)" + + " VALUES(?,?);"); + loadBooleanSetting(stmt, Settings.Global.GUEST_USER_ENABLED, + R.bool.def_guest_user_enabled); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + if (stmt != null) stmt.close(); + } + } + upgradeVersion = 105; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { @@ -2410,6 +2428,8 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName()); + loadBooleanSetting(stmt, Settings.Global.GUEST_USER_ENABLED, + R.bool.def_guest_user_enabled); // --- New global settings start here } finally { if (stmt != null) stmt.close(); diff --git a/packages/SystemUI/res/anim/recents_from_app_enter.xml b/packages/SystemUI/res/anim/recents_from_app_enter.xml new file mode 100644 index 0000000..6abe8b3 --- /dev/null +++ b/packages/SystemUI/res/anim/recents_from_app_enter.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2012, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false" + android:zAdjustment="top"> + <alpha android:fromAlpha="1.0" android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="0"/> +</set> diff --git a/packages/SystemUI/res/anim/recents_from_app_exit.xml b/packages/SystemUI/res/anim/recents_from_app_exit.xml new file mode 100644 index 0000000..1447a5a --- /dev/null +++ b/packages/SystemUI/res/anim/recents_from_app_exit.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2012, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false" + android:zAdjustment="normal"> + + <!-- Animate the view out only after recents is visible --> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="1"/> +</set> diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml index 4bd7e82..bac8cb6 100644 --- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml +++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml @@ -20,9 +20,9 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top"> - <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + <alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/accelerate_cubic" - android:duration="250"/> + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="100"/> </set> diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml index becc9d0..b0f8807 100644 --- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml +++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml @@ -23,6 +23,6 @@ <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true" - android:interpolator="@android:interpolator/decelerate_cubic" - android:duration="250"/> + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="100"/> </set> diff --git a/packages/SystemUI/res/anim/recents_from_unknown_enter.xml b/packages/SystemUI/res/anim/recents_from_unknown_enter.xml new file mode 100644 index 0000000..f68a143 --- /dev/null +++ b/packages/SystemUI/res/anim/recents_from_unknown_enter.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2012, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false" + android:zAdjustment="top"> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="200"/> +</set> diff --git a/packages/SystemUI/res/anim/recents_from_unknown_exit.xml b/packages/SystemUI/res/anim/recents_from_unknown_exit.xml new file mode 100644 index 0000000..31cf26a --- /dev/null +++ b/packages/SystemUI/res/anim/recents_from_unknown_exit.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2012, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false" + android:zAdjustment="normal"> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="200"/> +</set> diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml new file mode 100644 index 0000000..2857c04 --- /dev/null +++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2012, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false" + android:zAdjustment="normal"> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="150"/> +</set> diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml new file mode 100644 index 0000000..1139e72 --- /dev/null +++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2012, 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. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:shareInterpolator="false" + android:zAdjustment="top"> + <alpha android:fromAlpha="1.0" android:toAlpha="0.0" + android:fillEnabled="true" + android:fillBefore="true" android:fillAfter="true" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="150"/> +</set> diff --git a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index e6af81e..0000000 --- a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png Binary files differindex 873ca7e..fa5a0f0 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png Binary files differindex 9d3cf53..d88397e 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png Binary files differindex 288d36a..59d4695 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png Binary files differindex 266d34d..bbebcb1 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png Binary files differindex 0298054..93dc268 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png Binary files differindex 91e2edf..0073872 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png Binary files differindex 9ed15a7..0073872 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png Binary files differindex 6f2915b..8a3d69c 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png Binary files differindex 3f3b692..0afd6fe 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png Binary files differnew file mode 100644 index 0000000..d4fdbf3 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png diff --git a/packages/SystemUI/res/drawable-hdpi/recents_nav_bar_background.9.png b/packages/SystemUI/res/drawable-hdpi/recents_nav_bar_background.9.png Binary files differdeleted file mode 100644 index 6cd1176..0000000 --- a/packages/SystemUI/res/drawable-hdpi/recents_nav_bar_background.9.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index 5ce8708..0000000 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png Binary files differindex 3f7c4b0..6ca3652 100644 --- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png Binary files differindex 956bb7c..66f5c06 100644 --- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent.png Binary files differdeleted file mode 100644 index 86e2947..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index d8ab8ea..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png Binary files differindex a53aef1..e49a2af 100644 --- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png Binary files differindex e1b2145..84879b9 100644 --- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent.png Binary files differdeleted file mode 100644 index dffc059..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index c0209b0..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png Binary files differindex 2d8f81d..d005f61 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png Binary files differindex 61c6d11..dca30ab 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent.png Binary files differdeleted file mode 100644 index a67ed6d..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index 6101333..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png Binary files differindex 90eece6..6915a4a 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png Binary files differindex 382cf23..309fd4e 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent.png Binary files differdeleted file mode 100644 index ca1c8c4..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index cfef88d..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png Binary files differindex a4b3b37..23b570e 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png Binary files differindex ee5f623..1c5f73f 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent.png Binary files differdeleted file mode 100644 index ca3c541..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index 78d4490..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png Binary files differindex 3d21350..40cf808 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png Binary files differindex 40fbaec..36dd2be 100644 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent.png Binary files differdeleted file mode 100644 index 77bef31..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index d79f5b7..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png Binary files differindex 61b4569..3b979cd 100644 --- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png Binary files differindex 1a0312b..e8d1dea 100644 --- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent.png Binary files differdeleted file mode 100644 index d04916e..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index d940d34..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png Binary files differindex 8715a11..0ab0991 100644 --- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png Binary files differindex 487a0b1..4ced46b 100644 --- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent.png Binary files differdeleted file mode 100644 index a7cff47..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent_land.png Binary files differdeleted file mode 100644 index 0a60bb6..0000000 --- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent_land.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index e865f4c..0000000 --- a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png Binary files differindex 225d924..891397c 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png Binary files differindex 7779d57..5cf3855 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png Binary files differindex 37d17d2..edb5b1f 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png Binary files differindex 5e70a07..3212336 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png Binary files differindex 39a0c07..7aff620 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png Binary files differindex 5bbf3fe..9f66c14 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png Binary files differindex 798f62f..9f66c14 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png Binary files differindex f8e549a..972b1a8 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png Binary files differindex c7fda96..473b0cb 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png Binary files differnew file mode 100644 index 0000000..9fc1a3b --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png diff --git a/packages/SystemUI/res/drawable-mdpi/recents_nav_bar_background.9.png b/packages/SystemUI/res/drawable-mdpi/recents_nav_bar_background.9.png Binary files differdeleted file mode 100644 index 7237f09..0000000 --- a/packages/SystemUI/res/drawable-mdpi/recents_nav_bar_background.9.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index bdf0f67..0000000 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png Binary files differindex 3361e34..8ad60a5 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png Binary files differindex ed52bc3..17cc722 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png Binary files differindex b380327..6fa0d5f 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png Binary files differindex 49df31b..70b3ef6 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png Binary files differindex ac45bcd..be6a5ef 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png Binary files differindex 9fb4266..b26600d 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png Binary files differindex 459d011..b26600d 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png Binary files differindex e1cddde..f2cb7be 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png Binary files differindex e459f2c..170bdb6 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png Binary files differindex 0ec4d23..b4bea6d 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png Binary files differindex 4dedcbe..eb0eca1 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png Binary files differindex e55f2bf..3d8d19b 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png Binary files differindex a39c3e5..0f6fdad 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png Binary files differindex 80e988f..cbe5f77 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png Binary files differindex 4ef12b0..80eb803 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png Binary files differindex 8ef12a8..80eb803 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png Binary files differindex c2977c0..a6d892f 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png Binary files differindex 60ec10e..7183b81 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png Binary files differindex 8aa6e3a..657d546 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png Binary files differindex e272b62..df23594 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png Binary files differindex bf68b22..ab27aec 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png Binary files differindex 59ef663..31f98e1 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png Binary files differindex cf3a5ff..104aed7 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png Binary files differindex 2598954..18e113a 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png Binary files differindex 66853db..18e113a 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png Binary files differindex c46fd02..079e68d 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png Binary files differindex 0e84d92..c38b43a 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png Binary files differindex 7e96395e..c9507af 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png Binary files differindex cb94580..2426776 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png Binary files differindex 382ef39..48fb781 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png Binary files differindex 8d7be53..c6fb9bf 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png Binary files differindex 613fba0..528df6d 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png Binary files differindex 0511ad1..055070a 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png Binary files differindex 77fe9b2..055070a 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png Binary files differindex ae78eb0..e17e8f4 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png Binary files differindex 5b446b1..aafee03 100644 --- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index 83693c1..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png Binary files differindex a2bb50a..e3cf7a6 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png Binary files differindex 24897ce..7e034b8 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png Binary files differindex aaeeb1b..a4b3a68 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png Binary files differindex ddee461..321fadb 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png Binary files differindex 23a7997..6e3219f 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png Binary files differindex 9f200c2..2b6021b 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png Binary files differindex 43e9bc2..2b6021b 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png Binary files differindex 5c0ba82..023ca54 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png Binary files differindex b76a0ca..3c9dccb 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png Binary files differnew file mode 100644 index 0000000..f38de93 --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_nav_bar_background.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_nav_bar_background.9.png Binary files differdeleted file mode 100644 index 8d56a1d..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/recents_nav_bar_background.9.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index 461535c..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index cebbb15..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png Binary files differindex 79cfcee..d8f1703 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png Binary files differindex 7959f65..4e12cc9 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png Binary files differindex c3bfcfb..37951a0 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png Binary files differindex 64f6a22..7281482 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png Binary files differindex 8fd36bc..a13418c 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png Binary files differindex 6f30e54..066c2d9 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png Binary files differindex 024ef8f..a619a94 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png Binary files differindex 6e0b071..115109a 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png Binary files differindex 9a56987..3101cb0 100644..100755 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png Binary files differnew file mode 100644 index 0000000..8194605 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_nav_bar_background.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_nav_bar_background.9.png Binary files differdeleted file mode 100644 index aed300b..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/recents_nav_bar_background.9.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index 7b03a11..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/ic_qs_location_03.xml b/packages/SystemUI/res/drawable/ic_notify_zen.xml index 956a8c3..c46455b 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_03.xml +++ b/packages/SystemUI/res/drawable/ic_notify_zen.xml @@ -15,14 +15,14 @@ Copyright (C) 2014 The Android Open Source Project --> <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:width="24dp" + android:height="24dp"/> <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> <path android:fill="#FFFFFFFF" - android:pathData="M12.0,7.0c-2.9,0.0 -5.25,2.35 -5.25,5.25C6.75,16.19 12.0,22.0 12.0,22.0s5.25,-5.81 5.25,-9.75C17.25,9.35 14.9,7.0 12.0,7.0zM12.0,14.12c-1.04,0.0 -1.88,-0.84 -1.88,-1.88s0.84,-1.88 1.88,-1.88c1.04,0.0 1.87,0.84 1.87,1.88S13.04,14.12 12.0,14.12z"/> + android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_01.xml b/packages/SystemUI/res/drawable/ic_power_low.xml index ff37d9a..5bb7aba 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_01.xml +++ b/packages/SystemUI/res/drawable/ic_power_low.xml @@ -15,14 +15,14 @@ Copyright (C) 2014 The Android Open Source Project --> <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:width="24.0dp" + android:height="24.0dp"/> <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> <path android:fill="#FFFFFFFF" - android:pathData="M12.0,2.0C8.13,2.0 5.0,5.13 5.0,9.0c0.0,5.25 7.0,13.0 7.0,13.0s7.0,-7.75 7.0,-13.0C19.0,5.13 15.87,2.0 12.0,2.0zM12.0,11.5c-1.38,0.0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5c1.38,0.0 2.5,1.12 2.5,2.5S13.38,11.5 12.0,11.5z"/> + android:pathData="M30.0,6.0L30.0,2.0L18.0,2.0l0.0,4.0l-8.0,0.0l0.0,40.0l28.0,0.0L38.0,6.0L30.0,6.0zM26.0,37.0l-4.0,0.0l0.0,-4.0l4.0,0.0L26.0,37.0zM26.0,30.0l-4.0,0.0L22.0,15.0l4.0,0.0L26.0,30.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_02.xml b/packages/SystemUI/res/drawable/ic_power_saver.xml index bb4465f..26e7375 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_02.xml +++ b/packages/SystemUI/res/drawable/ic_power_saver.xml @@ -15,14 +15,14 @@ Copyright (C) 2014 The Android Open Source Project --> <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:width="24.0dp" + android:height="24.0dp"/> <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> <path android:fill="#FFFFFFFF" - android:pathData="M12.0,4.0c-3.48,0.0 -6.3,2.82 -6.3,6.3C5.7,15.02 12.0,22.0 12.0,22.0s6.3,-6.98 6.3,-11.7C18.3,6.82 15.48,4.0 12.0,4.0zM12.0,12.55c-1.24,0.0 -2.25,-1.01 -2.25,-2.25S10.76,8.05 12.0,8.05c1.24,0.0 2.25,1.01 2.25,2.25S13.24,12.55 12.0,12.55z"/> + android:pathData="M30.0,6.0L30.0,2.0L18.0,2.0l0.0,4.0l-8.0,0.0l0.0,40.0l28.0,0.0L38.0,6.0L30.0,6.0zM32.0,28.0l-6.0,0.0l0.0,6.0l-4.0,0.0l0.0,-6.0l-6.0,0.0l0.0,-4.0l6.0,0.0l0.0,-6.0l4.0,0.0l0.0,6.0l6.0,0.0L32.0,28.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml b/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml deleted file mode 100644 index dc30a53..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -Copyright (C) 2014 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:fill="#FFFFFFFF" - android:pathData="M18.939,7.244c-5.887,-5.885 -6.214,-6.214 -6.222,-6.222l-0.707,-0.737L5.088,7.207c-2.914,2.915 -3.74,6.629 -2.266,10.19c1.541,3.719 5.312,6.316 9.174,6.317l0.0,0.0c3.861,-0.001 7.636,-2.603 9.179,-6.328C22.646,13.834 21.832,10.138 18.939,7.244zM4.67,16.632c-1.149,-2.776 -0.481,-5.696 1.832,-8.011l5.494,-5.492c0.0,0.002 0.002,0.003 0.003,0.004l0.0,18.582c-0.001,0.0 -0.002,0.0 -0.003,0.0C8.922,21.714 5.91,19.624 4.67,16.632z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml new file mode 100644 index 0000000..b6a5cad --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml @@ -0,0 +1,28 @@ +<!-- +Copyright (C) 2014 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> + + <path + android:fill="#4DFFFFFF" + android:pathData="M41.3,41.7L36.6,37.0L24.0,24.5l-7.1,-7.1L14.0,14.5L8.5,9.0L6.0,11.5l5.6,5.6c-5.1,6.3 -4.7,15.5 1.1,21.4c3.1,3.1 7.2,4.7 11.3,4.7c3.6,0.0 7.1,-1.2 10.1,-3.6l5.4,5.4l2.5,-2.5L41.3,41.7zM24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5c0.0,-2.6 0.9,-5.1 2.4,-7.2l9.6,9.6L24.0,39.2zM24.0,10.2l0.0,9.2l14.5,14.5c2.7,-5.9 1.7,-13.1 -3.2,-18.0L24.0,4.5l0.0,0.0l0.0,0.0L16.6,12.0l2.8,2.8L24.0,10.2z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_04.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml index 0c0fb3b..e8d59e0 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_04.xml +++ b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml @@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project android:height="64dp"/> <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> <path android:fill="#FFFFFFFF" - android:pathData="M12.0,10.0c-2.32,0.0 -4.2,1.88 -4.2,4.2C7.8,17.35 12.0,22.0 12.0,22.0s4.2,-4.65 4.2,-7.8C16.2,11.88 14.32,10.0 12.0,10.0zM12.0,15.7c-0.83,0.0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5c0.83,0.0 1.5,0.67 1.5,1.5S12.83,15.7 12.0,15.7z"/> + android:pathData="M35.3,15.9L24.0,4.5l0.0,0.0l0.0,0.0L12.7,15.9c-6.2,6.2 -6.2,16.4 0.0,22.6c3.1,3.1 7.2,4.7 11.3,4.7s8.2,-1.6 11.3,-4.7C41.6,32.2 41.6,22.1 35.3,15.9zM24.0,39.2L24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5s1.2,-6.2 3.5,-8.5l8.5,-8.5L24.0,39.2z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_05.xml b/packages/SystemUI/res/drawable/ic_qs_location_05.xml deleted file mode 100644 index 1a21e2f..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_05.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -Copyright (C) 2014 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:fill="#FFFFFFFF" - android:pathData="M12.0,13.0c-1.74,0.0 -3.15,1.41 -3.15,3.15C8.85,18.51 12.0,22.0 12.0,22.0s3.15,-3.49 3.15,-5.85C15.15,14.41 13.74,13.0 12.0,13.0zM12.0,17.27c-0.62,0.0 -1.13,-0.5 -1.13,-1.12c0.0,-0.62 0.5,-1.12 1.13,-1.12c0.62,0.0 1.12,0.5 1.12,1.12C13.12,16.77 12.62,17.27 12.0,17.27z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_06.xml b/packages/SystemUI/res/drawable/ic_qs_location_06.xml deleted file mode 100644 index 5642a8a..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_06.xml +++ /dev/null @@ -1,31 +0,0 @@ -<!-- -Copyright (C) 2014 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:fill="#FFFFFFFF" - android:pathData="M12.0,16.0c-1.16,0.0 -2.1,0.94 -2.1,2.1C9.9,19.67 12.0,22.0 12.0,22.0s2.1,-2.33 2.1,-3.9C14.1,16.94 13.16,16.0 12.0,16.0zM12.0,18.85c-0.41,0.0 -0.75,-0.34 -0.75,-0.75s0.34,-0.75 0.75,-0.75c0.41,0.0 0.75,0.34 0.75,0.75S12.41,18.85 12.0,18.85z"/> - <path - android:pathData="M11.99,15c-1.35,0,-2.45,1.1,-2.45,2.45 c0,1.84,2.45,4.55,2.45,4.55s2.45,-2.71,2.45,-4.55C14.44,16.1,13.34,15,11.99,15z M11.99,18.33c-0.48,0,-0.88,-0.39,-0.88,-0.88 s0.39,-0.88,0.88,-0.88c0.48,0,0.87,0.39,0.87,0.88S12.47,18.33,11.99,18.33z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_07.xml b/packages/SystemUI/res/drawable/ic_qs_location_07.xml deleted file mode 100644 index 1ad2ebc..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_07.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -Copyright (C) 2014 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:pathData="M12,9c-2.51,0,-4.55,2.04,-4.55,4.55 C7.45,16.96,12,22,12,22s4.55,-5.04,4.55,-8.45C16.55,11.04,14.51,9,12,9z M12,15.18c-0.9,0,-1.63,-0.73,-1.63,-1.62 s0.73,-1.62,1.63,-1.62c0.9,0,1.62,0.73,1.62,1.62S12.9,15.18,12,15.18z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_08.xml b/packages/SystemUI/res/drawable/ic_qs_location_08.xml deleted file mode 100644 index 179bc66..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_08.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -Copyright (C) 2014 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:pathData="M12,6c-3.09,0,-5.6,2.51,-5.6,5.6 C6.4,15.8,12,22,12,22s5.6,-6.2,5.6,-10.4C17.6,8.51,15.09,6,12,6z M12,13.6c-1.1,0,-2,-0.9,-2,-2s0.9,-2,2,-2c1.1,0,2,0.9,2,2 S13.1,13.6,12,13.6z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_09.xml b/packages/SystemUI/res/drawable/ic_qs_location_09.xml deleted file mode 100644 index 6169af5..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_09.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -Copyright (C) 2014 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:pathData="M12,4c-3.48,0,-6.3,2.82,-6.3,6.3 C5.7,15.02,12,22,12,22s6.3,-6.98,6.3,-11.7C18.3,6.82,15.48,4,12,4z M12,12.55c-1.24,0,-2.25,-1.01,-2.25,-2.25S10.76,8.05,12,8.05 c1.24,0,2.25,1.01,2.25,2.25S13.24,12.55,12,12.55z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_10.xml b/packages/SystemUI/res/drawable/ic_qs_location_10.xml deleted file mode 100644 index 93e2eb4..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_10.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -Copyright (C) 2014 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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:pathData="M12,3C8.33,3,5.35,5.98,5.35,9.65 C5.35,14.64,12,22,12,22s6.65,-7.36,6.65,-12.35C18.65,5.98,15.67,3,12,3z M12,12.02c-1.31,0,-2.38,-1.06,-2.38,-2.38 S10.69,7.28,12,7.28c1.31,0,2.37,1.06,2.37,2.37S13.31,12.02,12,12.02z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_off.xml b/packages/SystemUI/res/drawable/ic_qs_location_off.xml index d28d347..26ebfbf 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_off.xml +++ b/packages/SystemUI/res/drawable/ic_qs_location_off.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2014 The Android Open Source Project @@ -14,18 +13,19 @@ Copyright (C) 2014 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<animation-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="true"> - <item android:drawable="@drawable/ic_qs_location_01" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_02" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_03" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_04" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_05" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_06" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_07" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_08" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_09" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_10" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_11" android:duration="16" /> -</animation-list> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> + + <path + android:fill="#4DFFFFFF" + android:pathData="M24.0,13.0c2.8,0.0 5.0,2.2 5.0,5.0c0.0,1.5 -0.7,2.8 -1.7,3.7l7.3,7.3c2.0,-3.7 3.4,-7.6 3.4,-11.0c0.0,-7.7 -6.3,-14.0 -14.0,-14.0c-4.0,0.0 -7.5,1.6 -10.1,4.3l6.4,6.4C21.2,13.6 22.5,13.0 24.0,13.0zM32.7,32.2l-9.3,-9.3l-0.2,-0.2L6.5,6.0L4.0,8.5l6.4,6.4c-0.2,1.0 -0.4,2.0 -0.4,3.1c0.0,10.5 14.0,26.0 14.0,26.0s3.3,-3.7 6.8,-8.7l6.7,6.7l2.5,-2.5L32.7,32.2z"/> + <path + android:pathData="M23.5,22.9l0.0,0.0 -0.20000076,-0.19999886z" + android:fill="#4DFFFFFF"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_on.xml b/packages/SystemUI/res/drawable/ic_qs_location_on.xml index 72512ac..bc73005 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_on.xml +++ b/packages/SystemUI/res/drawable/ic_qs_location_on.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2014 The Android Open Source Project @@ -14,18 +13,16 @@ Copyright (C) 2014 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<animation-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="true"> - <item android:drawable="@drawable/ic_qs_location_11" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_10" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_09" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_08" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_07" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_06" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_05" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_04" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_03" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_02" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_01" android:duration="16" /> -</animation-list> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M24.0,4.0c-7.7,0.0 -14.0,6.3 -14.0,14.0c0.0,10.5 14.0,26.0 14.0,26.0s14.0,-15.5 14.0,-26.0C38.0,10.3 31.7,4.0 24.0,4.0zM24.0,23.0c-2.8,0.0 -5.0,-2.2 -5.0,-5.0s2.2,-5.0 5.0,-5.0c2.8,0.0 5.0,2.2 5.0,5.0S26.8,23.0 24.0,23.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ripple_drawable.xml b/packages/SystemUI/res/drawable/ripple_drawable.xml index d2bff42..af7941f 100644 --- a/packages/SystemUI/res/drawable/ripple_drawable.xml +++ b/packages/SystemUI/res/drawable/ripple_drawable.xml @@ -16,6 +16,4 @@ --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" - android:tint="?android:attr/colorControlHighlight" - android:tintMode="src_over" - android:pinned="true" /> + android:color="?android:attr/colorControlHighlight" /> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_11.xml b/packages/SystemUI/res/drawable/stat_sys_no_sim.xml index 09a3e63..70948b7 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_11.xml +++ b/packages/SystemUI/res/drawable/stat_sys_no_sim.xml @@ -15,14 +15,14 @@ Copyright (C) 2014 The Android Open Source Project --> <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:width="18dp" + android:height="18dp"/> <viewport android:viewportWidth="24.0" android:viewportHeight="24.0"/> <path - android:pathData="M12,2C8.13,2,5,5.13,5,9c0,5.25,7,13,7,13s7,-7.75,7,-13 C19,5.13,15.87,2,12,2z M12,11.5c-1.38,0,-2.5,-1.12,-2.5,-2.5s1.12,-2.5,2.5,-2.5c1.38,0,2.5,1.12,2.5,2.5S13.38,11.5,12,11.5z" - android:fill="#4DFFFFFF"/> + android:fill="#4DFFFFFF" + android:pathData="M19.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-7.0,0.0L7.7,5.3L19.0,16.7L19.0,5.0zM3.7,3.9L2.4,5.2L5.0,7.8L5.0,19.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c0.4,0.0 0.7,-0.1 1.0,-0.3l1.9,1.9l1.3,-1.3L3.7,3.9z"/> </vector> diff --git a/packages/SystemUI/res/layout/battery_low.xml b/packages/SystemUI/res/layout/battery_low.xml deleted file mode 100644 index 2373355..0000000 --- a/packages/SystemUI/res/layout/battery_low.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* //device/apps/common/res/layout/keyguard.xml -** -** Copyright 2007, 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. -*/ ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/padding" - android:orientation="vertical" - android:gravity="center" - android:padding="16dp" - > - - <TextView android:id="@+id/subtitle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="18sp" - android:textColor="#ffffffff" - android:gravity="start" - android:text="@string/battery_low_subtitle" - /> - - <TextView android:id="@+id/level_percent" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="18sp" - android:textColor="#ffffffff" - android:gravity="start" - android:paddingBottom="16dp" - /> - - <ImageView android:id="@+id/image" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@drawable/battery_low_battery" - /> - -</LinearLayout> - - diff --git a/packages/SystemUI/res/layout/recents_nav_bar_scrim.xml b/packages/SystemUI/res/layout/recents_nav_bar_scrim.xml index 463fee8..4245d49 100644 --- a/packages/SystemUI/res/layout/recents_nav_bar_scrim.xml +++ b/packages/SystemUI/res/layout/recents_nav_bar_scrim.xml @@ -20,4 +20,4 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal|bottom" android:scaleType="fitXY" - android:src="@drawable/recents_nav_bar_background" />
\ No newline at end of file + android:src="@drawable/recents_lower_gradient" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml index 84d64b9..e220a16 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml @@ -18,48 +18,13 @@ <com.android.systemui.statusbar.SpeedBumpView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" - android:focusable="true" - android:clickable="true" + android:layout_height="@dimen/speed_bump_height" android:visibility="gone" > - <LinearLayout - android:layout_width="match_parent" - android:layout_height="@dimen/speed_bump_height_collapsed" - android:layout_gravity="top" - android:orientation="horizontal"> - <com.android.systemui.statusbar.AlphaOptimizedView - android:id="@+id/speedbump_line_left" - android:layout_width="0dp" - android:layout_height="1dp" - android:layout_weight="1" - android:background="#6fdddddd" - android:layout_gravity="center_vertical"/> - <com.android.systemui.statusbar.SpeedBumpDotsLayout - android:id="@+id/speed_bump_dots_layout" - android:layout_width="34dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:layout_height="match_parent" - android:layout_weight="0"/> - <com.android.systemui.statusbar.AlphaOptimizedView - android:id="@+id/speedbump_line_right" - android:layout_width="0dp" - android:layout_height="1dp" - android:layout_weight="1" - android:background="#6fdddddd" - android:layout_gravity="center_vertical"/> - </LinearLayout> - <TextView - android:id="@+id/speed_bump_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:fontFamily="sans-serif-condensed" - android:textSize="15sp" - android:singleLine="true" - android:textColor="#eeeeee" - android:visibility="invisible" - android:text="@string/speed_bump_explanation" - android:paddingTop="4dp" /> + <com.android.systemui.statusbar.AlphaOptimizedView + android:id="@+id/speedbump_line" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="#6fdddddd" + android:layout_gravity="center_vertical"/> </com.android.systemui.statusbar.SpeedBumpView> diff --git a/packages/SystemUI/res/layout/user_switcher_item.xml b/packages/SystemUI/res/layout/user_switcher_item.xml index 43a85e7..8df2f5a 100644 --- a/packages/SystemUI/res/layout/user_switcher_item.xml +++ b/packages/SystemUI/res/layout/user_switcher_item.xml @@ -21,10 +21,12 @@ android:layout_width="match_parent" android:layout_height="64dp" android:orientation="horizontal" + android:gravity="center_vertical" tools:context=".settings.UserSwitcherDialog"> <ImageView - android:layout_width="64dp" - android:layout_height="match_parent" + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginStart="4dp" android:id="@+id/user_picture" tools:src="@drawable/dessert_zombiegingerbread"/> <TextView @@ -37,4 +39,11 @@ android:gravity="center_vertical" tools:text="Hiroshi Lockheimer" /> + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:layout_marginEnd="4dp" + android:src="@*android:drawable/ic_menu_delete" + android:id="@+id/user_delete" + android:background="?android:attr/selectableItemBackground"/> </LinearLayout> diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml index 1ce4983..6628f3b 100644 --- a/packages/SystemUI/res/values/arrays.xml +++ b/packages/SystemUI/res/values/arrays.xml @@ -42,13 +42,11 @@ <!-- BatteryMeterView parameters --> <array name="batterymeter_color_levels"> - <item>4</item> <item>15</item> <item>100</item> </array> <array name="batterymeter_color_values"> <item>#FFFF3300</item> - <item>#FFFF3300</item> <item>#FFFFFFFF</item> </array> <array name="batterymeter_bolt_points"> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index bcd37bd..8c1a9c7 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -29,7 +29,6 @@ <drawable name="status_bar_notification_row_background_color">#ff090909</drawable> <color name="notification_list_shadow_top">#80000000</color> <drawable name="recents_callout_line">#99ffffff</drawable> - <drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable> <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable> <color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white --> <color name="batterymeter_charge_color">#FFFFFFFF</color> @@ -42,24 +41,10 @@ <color name="qs_tile_divider">#29ffffff</color><!-- 16% white --> <color name="qs_tile_text">#FFFFFFFF</color> <color name="status_bar_clock_color">#FFFFFFFF</color> - <drawable name="notification_item_background_color">#ff111111</drawable> - <drawable name="notification_item_background_color_pressed">#ff454545</drawable> <!-- Tint color for the content on the notification overflow card. --> <color name="keyguard_overflow_content_color">#ff686868</color> - <!-- The color of the red speed bump dot --> - <color name="speed_bump_dot_red">#ffd50000</color> - - <!-- The color of the blue speed bump dot --> - <color name="speed_bump_dot_blue">#ff2962ff</color> - - <!-- The color of the yellow speed bump dot --> - <color name="speed_bump_dot_yellow">#ffffd600</color> - - <!-- The color of the green speed bump dot --> - <color name="speed_bump_dot_green">#ff00c853</color> - <!-- The default recents task bar background color. --> <color name="recents_task_bar_default_background_color">#e6444444</color> <!-- The default recents task bar text color. --> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 1ef5bcd..b39fa84 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -113,12 +113,16 @@ <!-- The min animation duration for animating views that are newly visible. --> <integer name="recents_filter_animate_new_views_min_duration">125</integer> <!-- The min animation duration for animating the task bar in. --> - <integer name="recents_animate_task_bar_enter_duration">250</integer> + <integer name="recents_animate_task_bar_enter_duration">275</integer> <!-- The animation delay for animating the first task in. This should roughly be the animation duration of the transition in to recents. --> <integer name="recents_animate_task_bar_enter_delay">225</integer> <!-- The min animation duration for animating the task bar out. --> <integer name="recents_animate_task_bar_exit_duration">125</integer> + <!-- The min animation duration for animating the task in when transitioning from home. --> + <integer name="recents_animate_task_enter_from_home_duration">325</integer> + <!-- The animation stagger to apply to each task animation when transitioning from home. --> + <integer name="recents_animate_task_enter_from_home_delay">16</integer> <!-- The min animation duration for animating the nav bar scrim in. --> <integer name="recents_nav_bar_scrim_enter_duration">400</integer> <!-- The animation duration for animating the removal of a task view. --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 35bc7e3..5ffe3b3 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -235,9 +235,6 @@ <!-- The amount of highlight to make on each task view. --> <dimen name="recents_task_view_highlight">1dp</dimen> - <!-- The amount of space a user has to scroll to dismiss any info panes. --> - <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen> - <!-- The height of the search bar space. --> <dimen name="recents_search_bar_space_height">64dp</dimen> @@ -275,14 +272,8 @@ <!-- The minimum amount of top overscroll to go to the quick settings. --> <dimen name="min_top_overscroll_to_qs">36dp</dimen> - <!-- The height of the collapsed speed bump view. --> - <dimen name="speed_bump_height_collapsed">24dp</dimen> - - <!-- The padding inset the explanation text needs compared to the collapsed height --> - <dimen name="speed_bump_text_padding_inset">10dp</dimen> - - <!-- The height of the speed bump dots. --> - <dimen name="speed_bump_dots_height">5dp</dimen> + <!-- The height of the speed bump view. --> + <dimen name="speed_bump_height">16dp</dimen> <!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) --> <dimen name="collapsed_stack_height">94dp</dimen> @@ -338,4 +329,7 @@ <!-- The width of the region on the left/right edge of the screen for performing the camera/ phone hints. --> <dimen name="edge_tap_area_width">48dp</dimen> + + <!-- the distance the panel moves up when starting the up motion on Keyguard --> + <dimen name="keyguard_panel_move_up_distance">100dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ed32795..e5499ee 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -59,25 +59,43 @@ <string name="status_bar_latest_events_title">Notifications</string> <!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. [CHAR LIMIT=NONE]--> - <string name="battery_low_title">Connect charger</string> - - <!-- When the battery is low, this is displayed to the user in a dialog. The subtitle of the low battery alert. [CHAR LIMIT=NONE] --> - <string name="battery_low_subtitle">The battery is getting low.</string> + <string name="battery_low_title">Battery is low</string> <!-- A message that appears when the battery level is getting low in a dialog. This is appened to the subtitle of the low battery alert. "number" is the percentage of battery remaining [CHAR LIMIT=none]--> <string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g> remaining</string> + <!-- Same as battery_low_percent_format, with a notice about battery saver if on. [CHAR LIMIT=none]--> + <string name="battery_low_percent_format_saver_started"><xliff:g id="number">%d%%</xliff:g> remaining. Battery saver is on.</string> + <!-- A message that appears when a USB charger is plugged in and the device does not support charging on it. That is, a charger that fits into the USB port and goes into a wall socket, not into a computer. (This happens because some devices require more current than the USB spec allows. [CHAR LIMIT=NONE] --> <string name="invalid_charger">USB charging not supported.\nUse only the supplied charger.</string> + <!-- First line of invalid_charger, used in the notification form. [CHAR LIMIT=NONE]--> + <string name="invalid_charger_title">USB charging not supported.</string> + + <!-- Second line of invalid_charger, used in the notification form. [CHAR LIMIT=NONE]--> + <string name="invalid_charger_text">Use only the supplied charger.</string> + <!-- When the battery is low, this is the label of the button to go to the power usage activity to find out what drained the battery. [CHAR LIMIT=30] --> - <string name="battery_low_why">Battery use</string> + <string name="battery_low_why">Settings</string> + + <!-- Battery saver confirmation dialog title [CHAR LIMIT=NONE]--> + <string name="battery_saver_confirmation_title">Start battery saver?</string> + + <!-- Battery saver confirmation dialog ok text [CHAR LIMIT=40]--> + <string name="battery_saver_confirmation_ok">Start</string> + + <!-- Battery saver notification action [CHAR LIMIT=NONE]--> + <string name="battery_saver_start_action">Start battery saver</string> + + <!-- Battery saver confirmation dialog text [CHAR LIMIT=NONE]--> + <string name="battery_saver_confirmation_text">To help improve battery life, Battery saver will reduce your device’s performance.\n\nBattery saver will be disabled when your device is plugged in.</string> <!-- Name of the button that links to the Settings app. [CHAR LIMIT=NONE] --> <string name="status_bar_settings_settings_button">Settings</string> @@ -596,6 +614,13 @@ <!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]--> <string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string> + <!-- Related to user switcher --><skip/> + <!-- Name for the guest user --> + <string name="guest_nickname">Guest</string> + + <!-- Label for adding a new guest --> + <string name="guest_new_guest">+ Guest</string> + <!-- Zen mode condition: time duration in minutes. [CHAR LIMIT=NONE] --> <plurals name="zen_mode_duration_minutes"> <item quantity="one">For one minute</item> @@ -607,4 +632,13 @@ <item quantity="one">For one hour</item> <item quantity="other">For %d hours</item> </plurals> + + <!-- Battery saver notification title. [CHAR LIMIT=60]--> + <string name="battery_saver_notification_title">Battery saver is on</string> + + <!-- Battery saver notification text. [CHAR LIMIT=60] --> + <string name="battery_saver_notification_text">Device performance is reduced.</string> + + <!-- Battery saver notification action text. [CHAR LIMIT=60] --> + <string name="battery_saver_notification_action_text">Open battery saver settings</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 19d06be..5e48258 100755 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -25,8 +25,6 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.Typeface; import android.os.BatteryManager; @@ -44,7 +42,6 @@ public class BatteryMeterView extends View implements DemoMode { private static final boolean SHOW_100_PERCENT = false; private static final int FULL = 96; - private static final int EMPTY = 4; private static final float SUBPIXEL = 0.4f; // inset rects for softer edges private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction @@ -58,6 +55,7 @@ public class BatteryMeterView extends View implements DemoMode { private int mHeight; private int mWidth; private String mWarningString; + private final int mCriticalLevel; private final int mChargeColor; private final float[] mBoltPoints; private final Path mBoltPath = new Path(); @@ -197,6 +195,8 @@ public class BatteryMeterView extends View implements DemoMode { mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt( context.getContentResolver(), "status_bar_show_battery_percent", 0); mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol); + mCriticalLevel = mContext.getResources().getInteger( + com.android.internal.R.integer.config_criticalBatteryWarningLevel); mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mFramePaint.setColor(frameColor); @@ -303,7 +303,7 @@ public class BatteryMeterView extends View implements DemoMode { if (level >= FULL) { drawFrac = 1f; - } else if (level <= EMPTY) { + } else if (level <= mCriticalLevel) { drawFrac = 0f; } @@ -360,7 +360,7 @@ public class BatteryMeterView extends View implements DemoMode { boolean pctOpaque = false; float pctX = 0, pctY = 0; String pctText = null; - if (!tracker.plugged && level > EMPTY && mShowPercent + if (!tracker.plugged && level > mCriticalLevel && mShowPercent && !(tracker.level == 100 && !SHOW_100_PERCENT)) { mTextPaint.setColor(getColorForLevel(level)); mTextPaint.setTextSize(height * @@ -390,7 +390,7 @@ public class BatteryMeterView extends View implements DemoMode { c.drawPath(mShapePath, mBatteryPaint); if (!tracker.plugged) { - if (level <= EMPTY) { + if (level <= mCriticalLevel) { // draw the warning text final float x = mWidth * 0.5f; final float y = (mHeight + mWarningTextHeight) * 0.48f; diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java index 7c85712..9650435 100644 --- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java @@ -20,9 +20,14 @@ import android.view.Display; import android.view.View; public interface RecentsComponent { + public interface Callbacks { + public void onVisibilityChanged(boolean visible); + } + void showRecents(boolean triggeredFromAltTab, View statusBarView); void hideRecents(boolean triggeredFromAltTab); void toggleRecents(Display display, int layoutDirection, View statusBarView); void preloadRecents(); void cancelPreloadingRecents(); + void setCallback(Callbacks cb); } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java index feec87c..2943494 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java @@ -16,6 +16,7 @@ package com.android.systemui.power; +import android.app.ActivityManagerNative; import android.app.AlertDialog; import android.content.ContentResolver; import android.content.Context; @@ -25,13 +26,13 @@ import android.media.AudioManager; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; +import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; -import android.view.View; +import android.view.ContextThemeWrapper; import android.view.WindowManager; -import android.widget.TextView; import com.android.systemui.R; @@ -46,13 +47,14 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI { private int mBatteryLevel; private int mBucket; private long mScreenOffTime; + private boolean mSaver; + private int mSaverTriggerLevel; private AlertDialog mInvalidChargerDialog; private AlertDialog mLowBatteryDialog; - private TextView mBatteryLevelTextView; public PowerDialogWarnings(Context context) { - mContext = context; + mContext = new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_Light); } @Override @@ -77,7 +79,7 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI { @Override public void updateLowBatteryWarning() { - if (mBatteryLevelTextView != null) { + if (mLowBatteryDialog != null) { showLowBatteryWarning(false /*playSound*/); } } @@ -93,27 +95,22 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI { @Override public void showLowBatteryWarning(boolean playSound) { Slog.i(TAG, - ((mBatteryLevelTextView == null) ? "showing" : "updating") + ((mLowBatteryDialog == null) ? "showing" : "updating") + " low battery warning: level=" + mBatteryLevel + " [" + mBucket + "]"); - CharSequence levelText = mContext.getString( - R.string.battery_low_percent_format, mBatteryLevel); + final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started + : R.string.battery_low_percent_format; + final CharSequence levelText = mContext.getString(textRes, mBatteryLevel); - if (mBatteryLevelTextView != null) { - mBatteryLevelTextView.setText(levelText); + if (mLowBatteryDialog != null) { + mLowBatteryDialog.setMessage(levelText); } else { - View v = View.inflate(mContext, R.layout.battery_low, null); - mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent); - - mBatteryLevelTextView.setText(levelText); - AlertDialog.Builder b = new AlertDialog.Builder(mContext); - b.setCancelable(true); - b.setTitle(R.string.battery_low_title); - b.setView(v); - b.setIconAttribute(android.R.attr.alertDialogIcon); - b.setPositiveButton(android.R.string.ok, null); + b.setCancelable(true); + b.setTitle(R.string.battery_low_title); + b.setMessage(levelText); + b.setPositiveButton(android.R.string.ok, null); final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK @@ -125,6 +122,11 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI { new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + try { + ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); + } catch (RemoteException e) { + // we tried + } mContext.startActivityAsUser(intent, UserHandle.CURRENT); dismissLowBatteryWarning(); } @@ -136,10 +138,9 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI { @Override public void onDismiss(DialogInterface dialog) { mLowBatteryDialog = null; - mBatteryLevelTextView = null; } }); - d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); d.getWindow().getAttributes().privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; d.show(); @@ -198,21 +199,32 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI { dismissLowBatteryWarning(); AlertDialog.Builder b = new AlertDialog.Builder(mContext); - b.setCancelable(true); - b.setMessage(R.string.invalid_charger); - b.setIconAttribute(android.R.attr.alertDialogIcon); - b.setPositiveButton(android.R.string.ok, null); + b.setCancelable(true); + b.setTitle(R.string.invalid_charger_title); + b.setMessage(R.string.invalid_charger_text); + b.setPositiveButton(android.R.string.ok, null); AlertDialog d = b.create(); d.setOnDismissListener(new DialogInterface.OnDismissListener() { public void onDismiss(DialogInterface dialog) { mInvalidChargerDialog = null; - mBatteryLevelTextView = null; } }); - d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); + d.getWindow().getAttributes().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; d.show(); mInvalidChargerDialog = d; } + + @Override + public void showSaverMode(boolean mode) { + mSaver = mode; + } + + @Override + public void setSaverTrigger(int level) { + mSaverTriggerLevel = level; + } } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java new file mode 100644 index 0000000..e8f3745 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2014 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.power; + +import android.app.AlertDialog; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Slog; +import android.view.ContextThemeWrapper; +import android.view.View; +import android.view.WindowManager; + +import com.android.systemui.R; + +import java.io.PrintWriter; + +public class PowerNotificationWarnings implements PowerUI.WarningsUI { + private static final String TAG = PowerUI.TAG + ".Notification"; + private static final boolean DEBUG = PowerUI.DEBUG; + + private static final String TAG_NOTIFICATION = "low_battery"; + private static final int ID_NOTIFICATION = 100; + private static final int AUTO_DISMISS_MS = 10000; + + private static final int SHOWING_NOTHING = 0; + private static final int SHOWING_WARNING = 1; + private static final int SHOWING_SAVER = 2; + private static final int SHOWING_INVALID_CHARGER = 3; + private static final String[] SHOWING_STRINGS = { + "SHOWING_NOTHING", + "SHOWING_WARNING", + "SHOWING_SAVER", + "SHOWING_INVALID_CHARGER", + }; + + private static final String ACTION_SHOW_FALLBACK_WARNING = "PNW.warningFallback"; + private static final String ACTION_SHOW_FALLBACK_CHARGER = "PNW.chargerFallback"; + private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings"; + private static final String ACTION_START_SAVER = "PNW.startSaver"; + + private final Context mContext; + private final Context mLightContext; + private final NotificationManager mNoMan; + private final Handler mHandler = new Handler(); + private final PowerDialogWarnings mFallbackDialogs; + private final Receiver mReceiver = new Receiver(); + private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY); + private final Intent mOpenSaverSettings = settings(Settings.ACTION_BATTERY_SAVER_SETTINGS); + + private int mBatteryLevel; + private int mBucket; + private long mScreenOffTime; + private int mShowing; + + private boolean mSaver; + private int mSaverTriggerLevel; + private boolean mWarning; + private boolean mPlaySound; + private boolean mInvalidCharger; + + public PowerNotificationWarnings(Context context) { + mContext = context; + mLightContext = new ContextThemeWrapper(mContext, + android.R.style.Theme_DeviceDefault_Light); + mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + mFallbackDialogs = new PowerDialogWarnings(context); + mReceiver.init(); + } + + @Override + public void dump(PrintWriter pw) { + pw.print("mSaver="); pw.println(mSaver); + pw.print("mWarning="); pw.println(mWarning); + pw.print("mPlaySound="); pw.println(mPlaySound); + pw.print("mInvalidCharger="); pw.println(mInvalidCharger); + pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]); + } + + @Override + public void update(int batteryLevel, int bucket, long screenOffTime) { + mBatteryLevel = batteryLevel; + mBucket = bucket; + mScreenOffTime = screenOffTime; + mFallbackDialogs.update(batteryLevel, bucket, screenOffTime); + } + + @Override + public void showSaverMode(boolean mode) { + mSaver = mode; + updateNotification(); + } + + @Override + public void setSaverTrigger(int level) { + mSaverTriggerLevel = level; + updateNotification(); + } + + private void updateNotification() { + Slog.d(TAG, "updateNotification mWarning=" + mWarning + + " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger); + if (mInvalidCharger) { + showInvalidChargerNotification(); + mShowing = SHOWING_INVALID_CHARGER; + } else if (mWarning) { + showWarningNotification(); + mShowing = SHOWING_WARNING; + } else if (mSaver) { + showSaverNotification(); + mShowing = SHOWING_SAVER; + } else { + mNoMan.cancel(TAG_NOTIFICATION, ID_NOTIFICATION); + mShowing = SHOWING_NOTHING; + } + } + + private void showInvalidChargerNotification() { + final Notification.Builder nb = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_power_low) + .setShowWhen(false) + .setOngoing(true) + .setContentTitle(mContext.getString(R.string.invalid_charger_title)) + .setContentText(mContext.getString(R.string.invalid_charger_text)) + .setPriority(Notification.PRIORITY_MAX) + .setCategory(Notification.CATEGORY_SYSTEM) + .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_CHARGER), true); + final Notification n = nb.build(); + if (n.headsUpContentView != null) { + n.headsUpContentView.setViewVisibility(com.android.internal.R.id.right_icon, View.GONE); + } + mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, n, UserHandle.CURRENT); + } + + private void showWarningNotification() { + final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started + : R.string.battery_low_percent_format; + final Notification.Builder nb = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_power_low) + .setShowWhen(false) + .setContentTitle(mContext.getString(R.string.battery_low_title)) + .setContentText(mContext.getString(textRes, mBatteryLevel)) + .setOngoing(true) + .setPriority(Notification.PRIORITY_MAX) + .setCategory(Notification.CATEGORY_SYSTEM) + .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_WARNING), true); + if (hasBatterySettings()) { + nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS)); + } + if (!mSaver && mSaverTriggerLevel <= 0) { + nb.addAction(R.drawable.ic_power_saver, + mContext.getString(R.string.battery_saver_start_action), + pendingBroadcast(ACTION_START_SAVER)); + } + if (mPlaySound) { + attachLowBatterySound(nb); + } + final Notification n = nb.build(); + if (n.headsUpContentView != null) { + n.headsUpContentView.setViewVisibility(com.android.internal.R.id.right_icon, View.GONE); + } + mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, n, UserHandle.CURRENT); + } + + private void showSaverNotification() { + final Notification.Builder nb = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.ic_power_saver) + .setContentTitle(mContext.getString(R.string.battery_saver_notification_title)) + .setContentText(mContext.getString(R.string.battery_saver_notification_text)) + .setOngoing(true) + .setShowWhen(false) + .setCategory(Notification.CATEGORY_SYSTEM); + if (hasSaverSettings()) { + nb.addAction(0, + mContext.getString(R.string.battery_saver_notification_action_text), + pendingActivity(mOpenSaverSettings)); + nb.setContentIntent(pendingActivity(mOpenSaverSettings)); + } + mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, nb.build(), UserHandle.CURRENT); + } + + private PendingIntent pendingActivity(Intent intent) { + return PendingIntent.getActivityAsUser(mContext, + 0, intent, 0, null, UserHandle.CURRENT); + } + + private PendingIntent pendingBroadcast(String action) { + return PendingIntent.getBroadcastAsUser(mContext, + 0, new Intent(action), 0, UserHandle.CURRENT); + } + + private static Intent settings(String action) { + return new Intent(action).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_NO_HISTORY + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + } + + @Override + public boolean isInvalidChargerWarningShowing() { + return mInvalidCharger; + } + + @Override + public void updateLowBatteryWarning() { + updateNotification(); + mFallbackDialogs.updateLowBatteryWarning(); + } + + @Override + public void dismissLowBatteryWarning() { + Slog.i(TAG, "dismissing low battery warning: level=" + mBatteryLevel); + dismissLowBatteryNotification(); + mFallbackDialogs.dismissLowBatteryWarning(); + } + + private void dismissLowBatteryNotification() { + Slog.i(TAG, "dismissing low battery notification"); + mWarning = false; + updateNotification(); + } + + private boolean hasBatterySettings() { + return mOpenBatterySettings.resolveActivity(mContext.getPackageManager()) != null; + } + + private boolean hasSaverSettings() { + return mOpenSaverSettings.resolveActivity(mContext.getPackageManager()) != null; + } + + @Override + public void showLowBatteryWarning(boolean playSound) { + Slog.i(TAG, + "show low battery warning: level=" + mBatteryLevel + + " [" + mBucket + "]"); + mPlaySound = playSound; + mWarning = true; + updateNotification(); + mHandler.removeCallbacks(mDismissLowBatteryNotification); + mHandler.postDelayed(mDismissLowBatteryNotification, AUTO_DISMISS_MS); + } + + private void attachLowBatterySound(Notification.Builder b) { + final ContentResolver cr = mContext.getContentResolver(); + + final int silenceAfter = Settings.Global.getInt(cr, + Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0); + final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime; + if (silenceAfter > 0 + && mScreenOffTime > 0 + && offTime > silenceAfter) { + Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter + + "ms): not waking up the user with low battery sound"); + return; + } + + if (DEBUG) { + Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated + } + + if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) { + final String soundPath = Settings.Global.getString(cr, + Settings.Global.LOW_BATTERY_SOUND); + if (soundPath != null) { + final Uri soundUri = Uri.parse("file://" + soundPath); + if (soundUri != null) { + b.setSound(soundUri, AudioManager.STREAM_SYSTEM); + Slog.d(TAG, "playing sound " + soundUri); + } + } + } + } + + @Override + public void dismissInvalidChargerWarning() { + dismissInvalidChargerNotification(); + mFallbackDialogs.dismissInvalidChargerWarning(); + } + + private void dismissInvalidChargerNotification() { + Slog.i(TAG, "dismissing invalid charger notification"); + mInvalidCharger = false; + updateNotification(); + } + + @Override + public void showInvalidChargerWarning() { + mInvalidCharger = true; + updateNotification(); + } + + private void showStartSaverConfirmation() { + final AlertDialog d = new AlertDialog.Builder(mLightContext) + .setTitle(R.string.battery_saver_confirmation_title) + .setMessage(R.string.battery_saver_confirmation_text) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(R.string.battery_saver_confirmation_ok, mStartSaverMode) + .create(); + + d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); + d.getWindow().getAttributes().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + d.show(); + } + + private void setSaverSetting(boolean mode) { + final int val = mode ? 1 : 0; + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, val); + } + + private final class Receiver extends BroadcastReceiver { + + public void init() { + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_SHOW_FALLBACK_WARNING); + filter.addAction(ACTION_SHOW_FALLBACK_CHARGER); + filter.addAction(ACTION_SHOW_BATTERY_SETTINGS); + filter.addAction(ACTION_START_SAVER); + mContext.registerReceiver(this, filter, null, mHandler); + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + Slog.i(TAG, "Received " + action); + if (action.equals(ACTION_SHOW_FALLBACK_WARNING)) { + dismissLowBatteryNotification(); + mFallbackDialogs.showLowBatteryWarning(false /*playSound*/); + } else if (action.equals(ACTION_SHOW_FALLBACK_CHARGER)) { + dismissInvalidChargerNotification(); + mFallbackDialogs.showInvalidChargerWarning(); + } else if (action.equals(ACTION_SHOW_BATTERY_SETTINGS)) { + dismissLowBatteryNotification(); + mContext.startActivityAsUser(mOpenBatterySettings, UserHandle.CURRENT); + } else if (action.equals(ACTION_START_SAVER)) { + dismissLowBatteryNotification(); + showStartSaverConfirmation(); + } + } + } + + private final OnClickListener mStartSaverMode = new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + AsyncTask.execute(new Runnable() { + @Override + public void run() { + setSaverSetting(true); + } + }); + } + }; + + private final Runnable mDismissLowBatteryNotification = new Runnable() { + @Override + public void run() { + dismissLowBatteryNotification(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 0fb0f8b..1bb7edb 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -17,13 +17,17 @@ package com.android.systemui.power; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.Uri; import android.os.BatteryManager; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; +import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; @@ -35,13 +39,15 @@ import java.util.Arrays; public class PowerUI extends SystemUI { static final String TAG = "PowerUI"; - static final boolean DEBUG = false; - private WarningsUI mWarnings; private final Handler mHandler = new Handler(); + private final SettingsObserver mObserver = new SettingsObserver(mHandler); + private final Receiver mReceiver = new Receiver(); + private PowerManager mPowerManager; + private WarningsUI mWarnings; private int mBatteryLevel = 100; private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; private int mPlugType = 0; @@ -53,24 +59,54 @@ public class PowerUI extends SystemUI { private long mScreenOffTime = -1; public void start() { + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime(); + mWarnings = new PowerNotificationWarnings(mContext); - mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( - com.android.internal.R.integer.config_lowBatteryCloseWarningLevel); - mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( - com.android.internal.R.integer.config_lowBatteryWarningLevel); - mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( + ContentObserver obs = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + updateBatteryWarningLevels(); + } + }; + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), + false, obs, UserHandle.USER_ALL); + updateBatteryWarningLevels(); + mReceiver.init(); + mObserver.init(); + } + + private void setSaverMode(boolean mode) { + mWarnings.showSaverMode(mode); + } + + private void setSaverTrigger(int level) { + mWarnings.setSaverTrigger(level); + } + + void updateBatteryWarningLevels() { + int critLevel = mContext.getResources().getInteger( com.android.internal.R.integer.config_criticalBatteryWarningLevel); - final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime(); - mWarnings = new PowerDialogWarnings(mContext); + final ContentResolver resolver = mContext.getContentResolver(); + int defWarnLevel = mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryWarningLevel); + int warnLevel = Settings.Global.getInt(resolver, + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel); + if (warnLevel == 0) { + warnLevel = defWarnLevel; + } + if (warnLevel < critLevel) { + warnLevel = critLevel; + } - // Register for Intent broadcasts for... - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_CHANGED); - filter.addAction(Intent.ACTION_SCREEN_OFF); - filter.addAction(Intent.ACTION_SCREEN_ON); - mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); + mLowBatteryReminderLevels[0] = warnLevel; + mLowBatteryReminderLevels[1] = critLevel; + mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0] + + mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryCloseWarningBump); } /** @@ -87,7 +123,7 @@ public class PowerUI extends SystemUI { if (level >= mLowBatteryAlertCloseLevel) { return 1; } - if (level >= mLowBatteryReminderLevels[0]) { + if (level > mLowBatteryReminderLevels[0]) { return 0; } final int N = mLowBatteryReminderLevels.length; @@ -99,7 +135,23 @@ public class PowerUI extends SystemUI { throw new RuntimeException("not possible!"); } - private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + private final class Receiver extends BroadcastReceiver { + + public void init() { + // Register for Intent broadcasts for... + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + mContext.registerReceiver(this, filter, null, mHandler); + updateSaverMode(); + } + + private void updateSaverMode() { + setSaverMode(mPowerManager.isPowerSaveMode()); + } + @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); @@ -160,6 +212,8 @@ public class PowerUI extends SystemUI { mScreenOffTime = SystemClock.elapsedRealtime(); } else if (Intent.ACTION_SCREEN_ON.equals(action)) { mScreenOffTime = -1; + } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) { + updateSaverMode(); } else { Slog.w(TAG, "unknown intent: " + intent); } @@ -197,6 +251,8 @@ public class PowerUI extends SystemUI { public interface WarningsUI { void update(int batteryLevel, int bucket, long screenOffTime); + void setSaverTrigger(int level); + void showSaverMode(boolean mode); void dismissLowBatteryWarning(); void showLowBatteryWarning(boolean playSound); void dismissInvalidChargerWarning(); @@ -205,5 +261,29 @@ public class PowerUI extends SystemUI { boolean isInvalidChargerWarningShowing(); void dump(PrintWriter pw); } + + private final class SettingsObserver extends ContentObserver { + private final Uri LOW_POWER_MODE_TRIGGER_LEVEL_URI = + Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL); + + public SettingsObserver(Handler handler) { + super(handler); + } + + public void init() { + onChange(true, LOW_POWER_MODE_TRIGGER_LEVEL_URI); + final ContentResolver cr = mContext.getContentResolver(); + cr.registerContentObserver(LOW_POWER_MODE_TRIGGER_LEVEL_URI, false, this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (LOW_POWER_MODE_TRIGGER_LEVEL_URI.equals(uri)) { + final int level = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + setSaverTrigger(level); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index c76ee8c..786cd9e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -287,6 +287,7 @@ public abstract class QSTile<TState extends State> implements Listenable { public boolean activityIn; public boolean activityOut; public int overlayIconId; + public boolean filter; @Override public boolean copyTo(State other) { @@ -300,6 +301,7 @@ public abstract class QSTile<TState extends State> implements Listenable { o.activityIn = activityIn; o.activityOut = activityOut; o.overlayIconId = overlayIconId; + o.filter = filter; return super.copyTo(other) || changed; } @@ -311,6 +313,7 @@ public abstract class QSTile<TState extends State> implements Listenable { rt.insert(rt.length() - 1, ",activityIn=" + activityIn); rt.insert(rt.length() - 1, ",activityOut=" + activityOut); rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId); + rt.insert(rt.length() - 1, ",filter=" + filter); return rt; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java index 901cc10..d5fe033 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java @@ -93,12 +93,12 @@ public final class SignalTileView extends QSTileView { final SignalState s = (SignalState) state; mSignal.setImageDrawable(null); // force refresh mSignal.setImageResource(s.iconId); - mSignal.setColorFilter(FILTER); + mSignal.setColorFilter(s.filter ? FILTER : null); if (s.overlayIconId > 0) { mOverlay.setVisibility(VISIBLE); mOverlay.setImageDrawable(null); // force refresh mOverlay.setImageResource(s.overlayIconId); - mOverlay.setColorFilter(FILTER); + mOverlay.setColorFilter(s.filter ? FILTER : null); } else { mOverlay.setVisibility(GONE); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 182a0ce..6d91d33 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -72,12 +72,15 @@ public class CellularTile extends QSTile<QSTile.SignalState> { if (cb == null) return; final Resources r = mContext.getResources(); - state.iconId = cb.enabled && (cb.mobileSignalIconId > 0) + state.iconId = cb.noSim + ? R.drawable.stat_sys_no_sim + : cb.enabled && (cb.mobileSignalIconId > 0) ? cb.mobileSignalIconId : R.drawable.ic_qs_signal_no_signal; state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled ? cb.dataTypeIconId : 0; + state.filter = state.iconId != R.drawable.stat_sys_no_sim; state.activityIn = cb.enabled && cb.activityIn; state.activityOut = cb.enabled && cb.activityOut; @@ -117,6 +120,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { boolean activityIn; boolean activityOut; String enabledDesc; + boolean noSim; } private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() { @@ -134,7 +138,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { int mobileSignalIconId, String mobileSignalContentDescriptionId, int dataTypeIconId, boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description) { + String dataTypeContentDescriptionId, String description, boolean noSim) { final CallbackInfo info = new CallbackInfo(); // TODO pool? info.enabled = enabled; info.wifiEnabled = mWifiEnabled; @@ -145,6 +149,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { info.activityIn = activityIn; info.activityOut = activityOut; info.enabledDesc = description; + info.noSim = noSim; refreshState(info); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 5301362..7c2c7c3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -71,6 +71,6 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { state.visible = mVisible; state.value = enabled; state.label = mContext.getString(R.string.quick_settings_inversion_label); - state.iconId = R.drawable.ic_qs_color_inversion; + state.iconId = enabled ? R.drawable.ic_qs_inversion_on : R.drawable.ic_qs_inversion_off; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index db9b054..04f1eb5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -16,9 +16,6 @@ package com.android.systemui.qs.tiles; -import android.content.res.Resources; -import android.graphics.drawable.AnimationDrawable; - import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.policy.LocationController; @@ -63,28 +60,15 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { protected void handleUpdateState(BooleanState state, Object arg) { final boolean locationEnabled = mController.isLocationEnabled(); state.visible = true; - if (state.value != locationEnabled) { - state.value = locationEnabled; - final Resources res = mContext.getResources(); - final AnimationDrawable d = (AnimationDrawable) res.getDrawable(locationEnabled - ? R.drawable.ic_qs_location_on - : R.drawable.ic_qs_location_off); - state.icon = d; - mUiHandler.post(new Runnable() { - @Override - public void run() { - d.start(); - } - }); - } + state.value = locationEnabled; if (locationEnabled) { - if (state.icon == null) state.iconId = R.drawable.ic_qs_location_01; + state.iconId = R.drawable.ic_qs_location_on; state.label = mContext.getString(R.string.quick_settings_location_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_location, mContext.getString(R.string.accessibility_desc_on)); } else { - if (state.icon == null) state.iconId = R.drawable.ic_qs_location_11; + state.iconId = R.drawable.ic_qs_location_off; state.label = mContext.getString(R.string.quick_settings_location_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_location, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 6b73002..a236497 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -87,6 +87,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { state.connected = wifiConnected; state.activityIn = cb.enabled && cb.activityIn; state.activityOut = cb.enabled && cb.activityOut; + state.filter = true; final String signalContentDescription; final Resources r = mContext.getResources(); if (wifiConnected) { @@ -159,7 +160,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { int mobileSignalIconId, String mobileSignalContentDescriptionId, int dataTypeIconId, boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description) { + String dataTypeContentDescriptionId, String description, boolean noSim) { // noop } diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java index 116d755..e03c01c 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java @@ -273,6 +273,13 @@ public class Recents extends SystemUI implements RecentsComponent { } } + @Override + public void setCallback(Callbacks cb) { + if (mUseAlternateRecents) { + mAlternateRecents.setRecentsComponentCallback(cb); + } + } + /** * Send broadcast only if BOOT_COMPLETED */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index 2f6d58f..8861752 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -27,7 +27,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; @@ -35,16 +34,13 @@ import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; -import android.util.DisplayMetrics; -import android.view.Display; -import android.view.Surface; -import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; import com.android.systemui.R; +import com.android.systemui.RecentsComponent; +import java.lang.ref.WeakReference; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -129,8 +125,10 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta final public static int MSG_TOGGLE_RECENTS = 6; final public static int MSG_START_ENTER_ANIMATION = 7; - final public static String EXTRA_ANIMATING_WITH_THUMBNAIL = "recents.animatingWithThumbnail"; - final public static String EXTRA_FROM_ALT_TAB = "recents.triggeredFromAltTab"; + final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome"; + final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail"; + final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail"; + final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab"; final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration"; final public static String KEY_WINDOW_RECT = "recents.windowRect"; final public static String KEY_SYSTEM_INSETS = "recents.systemInsets"; @@ -138,7 +136,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect"; final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect"; - final static int sMinToggleDelay = 425; final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS"; @@ -146,6 +143,9 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity"; final static String sRecentsService = "com.android.systemui.recents.RecentsService"; + static Bitmap sLastScreenshot; + static RecentsComponent.Callbacks sRecentsComponentCallbacks; + Context mContext; SystemServicesProxy mSystemServicesProxy; @@ -213,15 +213,19 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta if (Console.Enabled) { Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]"); } + if (mServiceIsBound && mBootCompleted) { - // Notify recents to close it - try { - Bundle data = new Bundle(); - Message msg = Message.obtain(null, MSG_HIDE_RECENTS, triggeredFromAltTab ? 1 : 0, 0); - msg.setData(data); - mService.send(msg); - } catch (RemoteException re) { - re.printStackTrace(); + if (isRecentsTopMost(null)) { + // Notify recents to close it + try { + Bundle data = new Bundle(); + Message msg = Message.obtain(null, MSG_HIDE_RECENTS, + triggeredFromAltTab ? 1 : 0, 0); + msg.setData(data); + mService.send(msg); + } catch (RemoteException re) { + re.printStackTrace(); + } } } } @@ -343,80 +347,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta } } - /** Converts from the device rotation to the degree */ - float getDegreesForRotation(int value) { - switch (value) { - case Surface.ROTATION_90: - return 360f - 90f; - case Surface.ROTATION_180: - return 360f - 180f; - case Surface.ROTATION_270: - return 360f - 270f; - } - return 0f; - } - - /** Takes a screenshot of the surface */ - Bitmap takeScreenshot(Display display) { - DisplayMetrics dm = new DisplayMetrics(); - display.getRealMetrics(dm); - float[] dims = {dm.widthPixels, dm.heightPixels}; - float degrees = getDegreesForRotation(display.getRotation()); - boolean requiresRotation = (degrees > 0); - if (requiresRotation) { - // Get the dimensions of the device in its native orientation - Matrix m = new Matrix(); - m.preRotate(-degrees); - m.mapPoints(dims); - dims[0] = Math.abs(dims[0]); - dims[1] = Math.abs(dims[1]); - } - return SurfaceControl.screenshot((int) dims[0], (int) dims[1]); - } - - /** Creates the activity options for a thumbnail transition. */ - ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) { - // Loading from thumbnail - Bitmap thumbnail; - Bitmap firstThumbnail = loadFirstTaskThumbnail(); - if (firstThumbnail != null) { - // Create the thumbnail - thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(), - Bitmap.Config.ARGB_8888); - int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight()); - Canvas c = new Canvas(thumbnail); - c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size), - new Rect(0, 0, taskRect.width(), taskRect.height()), null); - c.setBitmap(null); - // Recycle the old thumbnail - firstThumbnail.recycle(); - } else { - // Load the thumbnail from the screenshot if can't get one from the system - WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - Display display = wm.getDefaultDisplay(); - Bitmap screenshot = takeScreenshot(display); - if (screenshot != null) { - Resources res = mContext.getResources(); - int size = Math.min(screenshot.getWidth(), screenshot.getHeight()); - int statusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); - thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(), - Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(thumbnail); - c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight + - size), new Rect(0, 0, taskRect.width(), taskRect.height()), null); - c.setBitmap(null); - // Recycle the temporary screenshot - screenshot.recycle(); - } else { - return null; - } - } - - return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, thumbnail, - taskRect.left, taskRect.top, this); - } - /** Returns whether the recents is currently running */ boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) { SystemServicesProxy ssp = mSystemServicesProxy; @@ -462,10 +392,12 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta mService.send(msg); // Time this path - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents"); - Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents"); + if (Console.Enabled) { + Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, + Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents"); + Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, + Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents"); + } } catch (RemoteException re) { re.printStackTrace(); } @@ -486,6 +418,68 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta } } + /** + * Creates the activity options for a unknown state->recents transition. + */ + ActivityOptions getUnknownTransitionActivityOptions() { + // Reset the last screenshot + consumeLastScreenshot(); + return ActivityOptions.makeCustomAnimation(mContext, + R.anim.recents_from_unknown_enter, + R.anim.recents_from_unknown_exit, mHandler, this); + } + + /** + * Creates the activity options for a home->recents transition. + */ + ActivityOptions getHomeTransitionActivityOptions() { + // Reset the last screenshot + consumeLastScreenshot(); + return ActivityOptions.makeCustomAnimation(mContext, + R.anim.recents_from_launcher_enter, + R.anim.recents_from_launcher_exit, mHandler, this); + } + + /** + * Creates the activity options for an app->recents transition. If this method sets the static + * screenshot, then we will use that for the transition. + */ + ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) { + // Recycle the last screenshot + consumeLastScreenshot(); + + // Take the full screenshot + if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { + sLastScreenshot = mSystemServicesProxy.takeScreenshot(); + if (sLastScreenshot != null) { + return ActivityOptions.makeCustomAnimation(mContext, + R.anim.recents_from_app_enter, + R.anim.recents_from_app_exit, mHandler, this); + } + } + + // If the screenshot fails, then load the first task thumbnail and use that + Bitmap firstThumbnail = loadFirstTaskThumbnail(); + if (firstThumbnail != null) { + // Create the new thumbnail for the animation down + // XXX: We should find a way to optimize this so we don't need to create a new bitmap + Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(), + Bitmap.Config.ARGB_8888); + int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight()); + Canvas c = new Canvas(thumbnail); + c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size), + new Rect(0, 0, taskRect.width(), taskRect.height()), null); + c.setBitmap(null); + // Recycle the old thumbnail + firstThumbnail.recycle(); + return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, + thumbnail, taskRect.left, taskRect.top, this); + } + + // If both the screenshot and thumbnail fails, then just fall back to the default transition + return getUnknownTransitionActivityOptions(); + } + /** Starts the recents activity */ void startRecentsActivity(boolean isTopTaskHome) { // If Recents is not the front-most activity and we should animate into it. If @@ -503,7 +497,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta // Try starting with a thumbnail transition ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect); if (opts != null) { - startAlternateRecentsActivity(opts, true); + if (sLastScreenshot != null) { + startAlternateRecentsActivity(opts, EXTRA_FROM_APP_FULL_SCREENSHOT); + } else { + startAlternateRecentsActivity(opts, EXTRA_FROM_APP_THUMBNAIL); + } } else { // Fall through below to the non-thumbnail transition useThumbnailTransition = false; @@ -512,25 +510,33 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta // If there is no thumbnail transition, then just use a generic transition if (!useThumbnailTransition) { - ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, - R.anim.recents_from_launcher_enter, - R.anim.recents_from_launcher_exit, mHandler, this); - startAlternateRecentsActivity(opts, false); + if (Constants.DebugFlags.App.EnableHomeTransition) { + ActivityOptions opts = getHomeTransitionActivityOptions(); + startAlternateRecentsActivity(opts, EXTRA_FROM_HOME); + } else { + ActivityOptions opts = getUnknownTransitionActivityOptions(); + startAlternateRecentsActivity(opts, null); + } } - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity"); + if (Console.Enabled) { + Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, + Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity"); + } mLastToggleTime = System.currentTimeMillis(); } /** Starts the recents activity */ - void startAlternateRecentsActivity(ActivityOptions opts, boolean animatingWithThumbnail) { + void startAlternateRecentsActivity(ActivityOptions opts, String extraFlag) { Intent intent = new Intent(sToggleRecentsAction); intent.setClassName(sRecentsPackage, sRecentsActivity); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); - intent.putExtra(EXTRA_ANIMATING_WITH_THUMBNAIL, animatingWithThumbnail); - intent.putExtra(EXTRA_FROM_ALT_TAB, mTriggeredFromAltTab); + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_TASK_ON_HOME); + if (extraFlag != null) { + intent.putExtra(extraFlag, true); + } + intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab); if (opts != null) { mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle( UserHandle.USER_CURRENT)); @@ -539,6 +545,30 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta } } + /** Returns the last screenshot taken, this will be called by the RecentsActivity. */ + public static Bitmap getLastScreenshot() { + return sLastScreenshot; + } + + /** Recycles the last screenshot taken, this will be called by the RecentsActivity. */ + public static void consumeLastScreenshot() { + if (sLastScreenshot != null) { + sLastScreenshot.recycle(); + sLastScreenshot = null; + } + } + + /** Sets the RecentsComponent callbacks. */ + public void setRecentsComponentCallback(RecentsComponent.Callbacks cb) { + sRecentsComponentCallbacks = cb; + } + + /** Notifies the callbacks that the visibility of Recents has changed. */ + public static void notifyVisibilityChanged(boolean visible) { + if (sRecentsComponentCallbacks != null) { + sRecentsComponentCallbacks.onVisibilityChanged(visible); + } + } /**** OnAnimationStartedListener Implementation ****/ diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index 76e88a5..cd4d206 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -25,6 +25,10 @@ public class Constants { public static final boolean Verbose = false; public static class App { + // Enables the home->Recents transition + public static final boolean EnableHomeTransition = false; + // Enables the screenshot app->Recents transition + public static final boolean EnableScreenshotAppTransition = false; // Enables the filtering of tasks according to their grouping public static final boolean EnableTaskFiltering = false; // Enables clipping of tasks against each other @@ -52,8 +56,11 @@ public class Constants { public static class App { public static final String TimeRecentsStartupKey = "startup"; public static final String TimeRecentsLaunchKey = "launchTask"; - public static final boolean TimeRecentsStartup = false; - public static final boolean TimeRecentsLaunchTask = false; + public static final String TimeRecentsScreenshotTransitionKey = "screenshot"; + public static final boolean TimeRecentsStartup = true; + public static final boolean TimeRecentsLaunchTask = true; + public static final boolean TimeRecentsScreenshotTransition = true; + public static final boolean RecentsComponent = false; public static final boolean TaskDataLoader = false; @@ -106,7 +113,7 @@ public class Constants { // The height of the peek space relative to the stack height public static final float StackPeekHeightPct = 0.1f; // The min scale of the last card in the peek area - public static final float StackPeekMinScale = 0.9f; + public static final float StackPeekMinScale = 0.8f; // The number of cards we see in the peek space public static final int StackPeekNumCards = 3; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 8680786..f9c219b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -22,11 +22,9 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Configuration; import android.os.Bundle; import android.util.Pair; import android.view.Gravity; @@ -34,15 +32,17 @@ import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.widget.FrameLayout; import com.android.systemui.R; import com.android.systemui.recents.model.SpaceNode; import com.android.systemui.recents.model.TaskStack; +import com.android.systemui.recents.views.FullScreenTransitionView; import com.android.systemui.recents.views.RecentsView; +import com.android.systemui.recents.views.ViewAnimation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.Set; /** Our special app widget host */ class RecentsAppWidgetHost extends AppWidgetHost { @@ -66,11 +66,16 @@ class RecentsAppWidgetHost extends AppWidgetHost { /* Activity */ public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks, - RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks { + RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks, + FullScreenTransitionView.FullScreenTransitionViewCallbacks { + FrameLayout mContainerView; RecentsView mRecentsView; View mEmptyView; View mNavBarScrimView; + FullScreenTransitionView mFullScreenshotView; + + RecentsConfiguration mConfig; AppWidgetHost mAppWidgetHost; AppWidgetProviderInfo mSearchAppWidgetInfo; @@ -79,6 +84,19 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView boolean mVisible; boolean mTaskLaunched; + private static Method sPropertyMethod; + static { + try { + Class<?> c = Class.forName("android.view.GLES20Canvas"); + sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class); + if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + // Broadcast receiver to handle messages from our RecentsService BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() { @Override @@ -93,8 +111,15 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Dismiss recents, launching the focused task dismissRecentsIfVisible(); } else { - // Otherwise, just finish the activity without launching any other activities - finish(); + // If we are mid-animation into Recents, then reverse it and finish + if (mFullScreenshotView == null || + !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) { + // Otherwise, just finish the activity without launching any other activities + ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context, + null, mFinishRunnable, null); + mRecentsView.startOnExitAnimation( + new ViewAnimation.TaskViewExitContext(exitTrigger)); + } } } else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) { // Try and unfilter and filtered stacks @@ -104,7 +129,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } } else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) { // Try and start the enter animation (or restart it on configuration changed) - mRecentsView.startOnEnterAnimation(); + mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView)); + // Call our callback + onEnterAnimationTriggered(); } } }; @@ -113,18 +140,37 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + // Mark recents as no longer visible + AlternateRecentsComponent.notifyVisibilityChanged(false); + // Finish without an animations finish(); } }; + // A runnable to finish the Recents activity + Runnable mFinishRunnable = new Runnable() { + @Override + public void run() { + // Mark recents as no longer visible + AlternateRecentsComponent.notifyVisibilityChanged(false); + // Finish with an animations + finish(); + overridePendingTransition(R.anim.recents_to_launcher_enter, + R.anim.recents_to_launcher_exit); + } + }; + /** Updates the set of recent tasks */ void updateRecentsTasks(Intent launchIntent) { // Update the configuration based on the launch intent - RecentsConfiguration config = RecentsConfiguration.getInstance(); - config.launchedWithThumbnailAnimation = launchIntent.getBooleanExtra( - AlternateRecentsComponent.EXTRA_ANIMATING_WITH_THUMBNAIL, false); - config.launchedFromAltTab = launchIntent.getBooleanExtra( - AlternateRecentsComponent.EXTRA_FROM_ALT_TAB, false); + mConfig.launchedFromHome = launchIntent.getBooleanExtra( + AlternateRecentsComponent.EXTRA_FROM_HOME, false); + mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra( + AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false); + mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra( + AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false); + mConfig.launchedWithAltTab = launchIntent.getBooleanExtra( + AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false); RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount); @@ -139,27 +185,17 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Add the default no-recents layout if (stacks.size() == 1 && stacks.get(0).getTaskCount() == 0) { mEmptyView.setVisibility(View.VISIBLE); - - // Dim the background even more - WindowManager.LayoutParams wlp = getWindow().getAttributes(); - wlp.dimAmount = Constants.Values.Window.DarkBackgroundDim; - getWindow().setAttributes(wlp); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); } else { mEmptyView.setVisibility(View.GONE); - - // Un-dim the background - WindowManager.LayoutParams wlp = getWindow().getAttributes(); - wlp.dimAmount = 0f; - getWindow().setAttributes(wlp); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); } + + // Dim the background + mRecentsView.setBackgroundColor(0x80000000); } /** Attempts to allocate and bind the search bar app widget */ void bindSearchBarAppWidget() { if (Constants.DebugFlags.App.EnableSearchLayout) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); // Reset the host view and widget info @@ -167,7 +203,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mSearchAppWidgetInfo = null; // Try and load the app widget id from the settings - int appWidgetId = config.searchBarAppWidgetId; + int appWidgetId = mConfig.searchBarAppWidgetId; if (appWidgetId >= 0) { mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId); if (mSearchAppWidgetInfo == null) { @@ -197,7 +233,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } // Save the app widget id into the settings - config.updateSearchBarAppWidgetId(this, widgetInfo.first); + mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first); mSearchAppWidgetInfo = widgetInfo.second; } } @@ -207,8 +243,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView /** Creates the search bar app widget view */ void addSearchBarAppWidgetView() { if (Constants.DebugFlags.App.EnableSearchLayout) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - int appWidgetId = config.searchBarAppWidgetId; + int appWidgetId = mConfig.searchBarAppWidgetId; if (appWidgetId >= 0) { if (Console.Enabled) { Console.log(Constants.Log.App.SystemUIHandshake, @@ -234,9 +269,19 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView /** Dismisses recents if we are already visible and the intent is to toggle the recents view */ boolean dismissRecentsIfVisible() { if (mVisible) { - if (!mRecentsView.launchFocusedTask()) { - if (!mRecentsView.launchFirstTask()) { - finish(); + // If we are mid-animation into Recents, then reverse it and finish + if (mFullScreenshotView == null || + !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) { + // If we have a focused task, then launch that task + if (!mRecentsView.launchFocusedTask()) { + // If there are any tasks, then launch the first task + if (!mRecentsView.launchFirstTask()) { + // We really shouldn't hit this, but if we do, just animate out (aka. finish) + ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, + null, mFinishRunnable, null); + mRecentsView.startOnExitAnimation( + new ViewAnimation.TaskViewExitContext(exitTrigger)); + } } } return true; @@ -258,7 +303,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Initialize the loader and the configuration RecentsTaskLoader.initialize(this); - RecentsConfiguration.reinitialize(this); + mConfig = RecentsConfiguration.reinitialize(this); // Initialize the widget host (the host id is static and does not change) mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId, this); @@ -277,16 +322,32 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView LayoutInflater inflater = LayoutInflater.from(this); mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false); mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, mContainerView, false); + mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM)); + if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { + mFullScreenshotView = new FullScreenTransitionView(this, this); + mFullScreenshotView.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + } mContainerView = new FrameLayout(this); mContainerView.addView(mRecentsView); mContainerView.addView(mEmptyView); + if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { + mContainerView.addView(mFullScreenshotView); + } mContainerView.addView(mNavBarScrimView); setContentView(mContainerView); // Update the recent tasks updateRecentsTasks(getIntent()); + // Prepare the screenshot transition if necessary + if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { + mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); + } + // Bind the search app widget when we first start up bindSearchBarAppWidget(); // Add the search bar layout @@ -296,11 +357,23 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView if (savedInstanceState != null) { onConfigurationChange(); } + + // XXX: Update the shadows + try { + sPropertyMethod.invoke(null, "ambientShadowStrength", String.valueOf(35f)); + sPropertyMethod.invoke(null, "ambientRatio", String.valueOf(0.5f)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } } void onConfigurationChange() { // Try and start the enter animation (or restart it on configuration changed) - mRecentsView.startOnEnterAnimation(); + mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView)); + // Call our callback + onEnterAnimationTriggered(); } @Override @@ -319,11 +392,16 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Initialize the loader and the configuration RecentsTaskLoader.initialize(this); - RecentsConfiguration.reinitialize(this); + mConfig = RecentsConfiguration.reinitialize(this); // Update the recent tasks updateRecentsTasks(intent); + // Prepare the screenshot transition if necessary + if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { + mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); + } + // Don't attempt to rebind the search bar widget, but just add the search bar layout addSearchBarAppWidgetView(); } @@ -337,8 +415,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView super.onStart(); // Start listening for widget package changes if there is one bound - RecentsConfiguration config = RecentsConfiguration.getInstance(); - if (config.searchBarAppWidgetId >= 0) { + if (mConfig.searchBarAppWidgetId >= 0) { mAppWidgetHost.startListening(); } @@ -412,8 +489,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView super.onStop(); // Stop listening for widget package changes if there was one bound - RecentsConfiguration config = RecentsConfiguration.getInstance(); - if (config.searchBarAppWidgetId >= 0) { + if (mConfig.searchBarAppWidgetId >= 0) { mAppWidgetHost.stopListening(); } @@ -452,54 +528,85 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void onBackPressed() { - // Unfilter any stacks - if (!mRecentsView.unfilterFilteredStacks()) { - if (!mRecentsView.launchFirstTask()) { - super.onBackPressed(); + // If we are mid-animation into Recents, then reverse it and finish + if (mFullScreenshotView == null || + !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) { + // If we are currently filtering in any stacks, unfilter them first + if (!mRecentsView.unfilterFilteredStacks()) { + if (mConfig.launchedFromHome) { + // Just start the animation out of recents + ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, + null, mFinishRunnable, null); + mRecentsView.startOnExitAnimation( + new ViewAnimation.TaskViewExitContext(exitTrigger)); + } else { + // Otherwise, try and launch the first task + if (!mRecentsView.launchFirstTask()) { + // If there are no tasks, then just finish recents + ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, + null, mFinishRunnable, null); + mRecentsView.startOnExitAnimation( + new ViewAnimation.TaskViewExitContext(exitTrigger)); + } + } } } } - @Override public void onEnterAnimationTriggered() { // Fade in the scrim - RecentsConfiguration config = RecentsConfiguration.getInstance(); - if (config.hasNavBarScrim()) { + if (mConfig.hasNavBarScrim()) { mNavBarScrimView.setVisibility(View.VISIBLE); mNavBarScrimView.setAlpha(0f); mNavBarScrimView.animate().alpha(1f) - .setStartDelay(config.taskBarEnterAnimDelay) - .setDuration(config.navBarScrimEnterDuration) - .setInterpolator(config.fastOutSlowInInterpolator) + .setStartDelay(mConfig.taskBarEnterAnimDelay) + .setDuration(mConfig.navBarScrimEnterDuration) + .setInterpolator(mConfig.fastOutSlowInInterpolator) .withLayer() .start(); } } @Override + public void onEnterAnimationComplete(boolean canceled) { + if (!canceled) { + // Reset the full screenshot transition view + if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { + mFullScreenshotView.reset(); + } + + // XXX: We should clean up the screenshot in this case as well, but it needs to happen + // after to animate up + } + // Recycle the full screen screenshot + AlternateRecentsComponent.consumeLastScreenshot(); + } + + @Override public void onTaskLaunching(boolean isTaskInStackBounds) { mTaskLaunched = true; // Fade out the scrim - RecentsConfiguration config = RecentsConfiguration.getInstance(); - if (!isTaskInStackBounds && config.hasNavBarScrim()) { + if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) { mNavBarScrimView.animate().alpha(0f) .setStartDelay(0) - .setDuration(config.taskBarExitAnimDuration) - .setInterpolator(config.fastOutSlowInInterpolator) + .setDuration(mConfig.taskBarExitAnimDuration) + .setInterpolator(mConfig.fastOutSlowInInterpolator) .withLayer() .start(); } + + // Mark recents as no longer visible + AlternateRecentsComponent.notifyVisibilityChanged(false); } @Override public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); - if (appWidgetId > -1 && appWidgetId == config.searchBarAppWidgetId) { + if (appWidgetId > -1 && appWidgetId == mConfig.searchBarAppWidgetId) { // The search provider may have changed, so just delete the old widget and bind it again ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId); - config.updateSearchBarAppWidgetId(this, -1); + mConfig.updateSearchBarAppWidgetId(this, -1); // Load the widget again bindSearchBarAppWidget(); addSearchBarAppWidgetView(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 0cf6ee6..a0c5253 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -37,27 +37,39 @@ public class RecentsConfiguration { DisplayMetrics mDisplayMetrics; - public Rect systemInsets = new Rect(); - public Rect displayRect = new Rect(); - - boolean isLandscape; - boolean transposeRecentsLayoutWithOrientation; - int searchBarAppWidgetId = -1; - + /** Animations */ public float animationPxMovementPerSecond; + /** Interpolators */ public Interpolator fastOutSlowInInterpolator; public Interpolator fastOutLinearInInterpolator; public Interpolator linearOutSlowInInterpolator; + public Interpolator quintOutInterpolator; + /** Filtering */ public int filteringCurrentViewsMinAnimDuration; public int filteringNewViewsMinAnimDuration; - public int taskStackScrollDismissInfoPaneDistance; + /** Insets */ + public Rect systemInsets = new Rect(); + public Rect displayRect = new Rect(); + + /** Layout */ + boolean isLandscape; + boolean transposeRecentsLayoutWithOrientation; + + /** Search bar */ + int searchBarAppWidgetId = -1; + public int searchBarSpaceHeightPx; + + /** Task stack */ public int taskStackMaxDim; - public float taskStackWidthPaddingPct; public int taskStackTopPaddingPx; + public float taskStackWidthPaddingPct; + /** Task view animation and styles */ + public int taskViewEnterFromHomeDuration; + public int taskViewEnterFromHomeDelay; public int taskViewRemoveAnimDuration; public int taskViewRemoveAnimTranslationXPx; public int taskViewTranslationZMinPx; @@ -66,23 +78,28 @@ public class RecentsConfiguration { public int taskViewRoundedCornerRadiusPx; public int taskViewHighlightPx; - public int searchBarSpaceHeightPx; - + /** Task bar colors */ public int taskBarViewDefaultBackgroundColor; public int taskBarViewDefaultTextColor; public int taskBarViewLightTextColor; public int taskBarViewDarkTextColor; public int taskBarViewHighlightColor; + /** Task bar animations */ public int taskBarEnterAnimDuration; public int taskBarEnterAnimDelay; public int taskBarExitAnimDuration; + /** Nav bar scrim */ public int navBarScrimEnterDuration; - public boolean launchedFromAltTab; - public boolean launchedWithThumbnailAnimation; + /** Launch states */ + public boolean launchedWithAltTab; + public boolean launchedFromAppWithThumbnail; + public boolean launchedFromAppWithScreenshot; + public boolean launchedFromHome; + /** Dev options */ public boolean developerOptionsEnabled; /** Private constructor */ @@ -108,33 +125,54 @@ public class RecentsConfiguration { DisplayMetrics dm = res.getDisplayMetrics(); mDisplayMetrics = dm; - isLandscape = res.getConfiguration().orientation == - Configuration.ORIENTATION_LANDSCAPE; - transposeRecentsLayoutWithOrientation = - res.getBoolean(R.bool.recents_transpose_layout_with_orientation); - if (Console.Enabled) { - Console.log(Constants.Log.UI.MeasureAndLayout, - "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait", - Console.AnsiGreen); - } - - displayRect.set(0, 0, dm.widthPixels, dm.heightPixels); + // Animations animationPxMovementPerSecond = res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second); + + // Interpolators + fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); + fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_linear_in); + linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.linear_out_slow_in); + quintOutInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.decelerate_quint); + + // Filtering filteringCurrentViewsMinAnimDuration = res.getInteger(R.integer.recents_filter_animate_current_views_min_duration); filteringNewViewsMinAnimDuration = res.getInteger(R.integer.recents_filter_animate_new_views_min_duration); - taskStackScrollDismissInfoPaneDistance = res.getDimensionPixelSize( - R.dimen.recents_task_stack_scroll_dismiss_info_pane_distance); - taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim); + // Insets + displayRect.set(0, 0, dm.widthPixels, dm.heightPixels); + + // Layout + isLandscape = res.getConfiguration().orientation == + Configuration.ORIENTATION_LANDSCAPE; + transposeRecentsLayoutWithOrientation = + res.getBoolean(R.bool.recents_transpose_layout_with_orientation); + // Search bar + searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height); + + // Update the search widget id + SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0); + searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1); + + // Task stack TypedValue widthPaddingPctValue = new TypedValue(); res.getValue(R.dimen.recents_stack_width_padding_percentage, widthPaddingPctValue, true); taskStackWidthPaddingPct = widthPaddingPctValue.getFloat(); + taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim); taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding); + // Task view animation and styles + taskViewEnterFromHomeDuration = + res.getInteger(R.integer.recents_animate_task_enter_from_home_duration); + taskViewEnterFromHomeDelay = + res.getInteger(R.integer.recents_animate_task_enter_from_home_delay); taskViewRemoveAnimDuration = res.getInteger(R.integer.recents_animate_task_view_remove_duration); taskViewRemoveAnimTranslationXPx = @@ -148,8 +186,7 @@ public class RecentsConfiguration { taskViewShadowOutlineBottomInsetPx = res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset); - searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height); - + // Task bar colors taskBarViewDefaultBackgroundColor = res.getColor(R.color.recents_task_bar_default_background_color); taskBarViewDefaultTextColor = @@ -161,6 +198,7 @@ public class RecentsConfiguration { taskBarViewHighlightColor = res.getColor(R.color.recents_task_bar_highlight_color); + // Task bar animations taskBarEnterAnimDuration = res.getInteger(R.integer.recents_animate_task_bar_enter_duration); taskBarEnterAnimDelay = @@ -168,24 +206,20 @@ public class RecentsConfiguration { taskBarExitAnimDuration = res.getInteger(R.integer.recents_animate_task_bar_exit_duration); + // Nav bar scrim navBarScrimEnterDuration = res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration); - fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_slow_in); - fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_linear_in); - linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.linear_out_slow_in); - // Check if the developer options are enabled ContentResolver cr = context.getContentResolver(); developerOptionsEnabled = Settings.Global.getInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; - // Update the search widget id - SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0); - searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1); + if (Console.Enabled) { + Console.log(Constants.Log.UI.MeasureAndLayout, + "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait", + Console.AnsiGreen); + } } /** Updates the system insets */ @@ -204,8 +238,10 @@ public class RecentsConfiguration { /** Called when the configuration has changed, and we want to reset any configuration specific * members. */ public void updateOnConfigurationChange() { - launchedFromAltTab = false; - launchedWithThumbnailAnimation = false; + launchedWithAltTab = false; + launchedFromAppWithThumbnail = false; + launchedFromAppWithScreenshot = false; + launchedFromHome = false; } /** Returns whether the search bar app widget exists. */ @@ -257,15 +293,4 @@ public class RecentsConfiguration { searchBarSpaceBounds.set(0, 0, width, searchBarSpaceHeightPx); } } - - /** Converts from DPs to PXs */ - public int pxFromDp(float size) { - return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - size, mDisplayMetrics)); - } - /** Converts from SPs to PXs */ - public int pxFromSp(float size) { - return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, - size, mDisplayMetrics)); - } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java index 0c2c11d..e554af7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java @@ -121,6 +121,7 @@ class SystemUIMessageHandler extends Handler { // Send a broadcast to hide recents Intent intent = new Intent(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY); intent.setPackage(context.getPackageName()); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); if (msg.arg1 != 0) { intent.putExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, true); } @@ -129,17 +130,21 @@ class SystemUIMessageHandler extends Handler { // Send a broadcast to toggle recents Intent intent = new Intent(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY); intent.setPackage(context.getPackageName()); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); context.sendBroadcast(intent); // Time this path - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents"); - Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents"); + if (Console.Enabled) { + Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, + Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents"); + Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, + Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents"); + } } else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) { // Send a broadcast to start the enter animation Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION); intent.setPackage(context.getPackageName()); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); context.sendBroadcast(intent); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index 4685186..dbcdb94 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -478,7 +478,7 @@ public class RecentsTaskLoader { // Load the thumbnail (if possible and not the foremost task, from the cache) if (!isForemostTask) { task.thumbnail = mThumbnailCache.get(task.key); - if (task.thumbnail != null) { + if (task.thumbnail != null && !tasksToForceLoad.contains(task)) { // Even though we get things from the cache, we should update them if // they've changed in the bg tasksToForceLoad.add(task); @@ -489,6 +489,7 @@ public class RecentsTaskLoader { Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|loadingTaskThumbnail]"); } + task.thumbnail = ssp.getTaskThumbnail(task.key.id); if (task.thumbnail != null) { task.thumbnail.setHasAlpha(false); @@ -512,20 +513,6 @@ public class RecentsTaskLoader { "" + (System.currentTimeMillis() - t1) + "ms"); } - /* - // Get all the stacks - t1 = System.currentTimeMillis(); - List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos(); - Console.log(Constants.Log.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms"); - Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size()); - for (ActivityManager.StackInfo s : stackInfos) { - Console.log(Constants.Log.App.TaskDataLoader, " [RecentsTaskLoader|stack]", s.toString()); - if (stacks.containsKey(s.stackId)) { - stacks.get(s.stackId).setRect(s.bounds); - } - } - */ - // Start the task loader mLoader.start(context); diff --git a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java new file mode 100644 index 0000000..2f89e6d2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014 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.recents; + +import android.content.Context; + +/** + * A ref counted trigger that does some logic when the count is first incremented, or last + * decremented. Not thread safe as it's not currently needed. + */ +public class ReferenceCountedTrigger { + + Context mContext; + int mCount; + Runnable mFirstIncRunnable; + Runnable mLastDecRunnable; + Runnable mErrorRunnable; + + // Convenience runnables + Runnable mIncrementRunnable = new Runnable() { + @Override + public void run() { + increment(); + } + }; + Runnable mDecrementRunnable = new Runnable() { + @Override + public void run() { + decrement(); + } + }; + + public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable, + Runnable lastDecRunnable, Runnable errorRunanable) { + mContext = context; + mFirstIncRunnable = firstIncRunnable; + mLastDecRunnable = lastDecRunnable; + mErrorRunnable = errorRunanable; + } + + /** Increments the ref count */ + public void increment() { + if (mCount == 0 && mFirstIncRunnable != null) { + mFirstIncRunnable.run(); + } + mCount++; + } + + /** Convenience method to increment this trigger as a runnable */ + public Runnable incrementAsRunnable() { + return mIncrementRunnable; + } + + /** Decrements the ref count */ + public void decrement() { + mCount--; + if (mCount == 0 && mLastDecRunnable != null) { + mLastDecRunnable.run(); + } else if (mCount < 0) { + if (mErrorRunnable != null) { + mErrorRunnable.run(); + } else { + new Throwable("Invalid ref count").printStackTrace(); + Console.logError(mContext, "Invalid ref count"); + } + } + } + + /** Convenience method to decrement this trigger as a runnable */ + public Runnable decrementAsRunnable() { + return mDecrementRunnable; + } + + /** Returns the current ref count */ + public int getCount() { + return mCount; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java index 7a3ffb8..f532aa0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java @@ -30,13 +30,20 @@ import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.graphics.Bitmap; +import android.graphics.Matrix; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.util.DisplayMetrics; import android.util.Pair; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Surface; +import android.view.SurfaceControl; +import android.view.WindowManager; import java.util.ArrayList; import java.util.Iterator; @@ -54,6 +61,8 @@ public class SystemServicesProxy { IPackageManager mIpm; UserManager mUm; SearchManager mSm; + WindowManager mWm; + Display mDisplay; String mRecentsPackage; ComponentName mAssistComponent; @@ -67,6 +76,8 @@ public class SystemServicesProxy { mUm = (UserManager) context.getSystemService(Context.USER_SERVICE); mIpm = AppGlobals.getPackageManager(); mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE); + mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mDisplay = mWm.getDefaultDisplay(); mRecentsPackage = context.getPackageName(); // Resolve the assist intent @@ -325,4 +336,13 @@ public class SystemServicesProxy { // Delete the app widget host.deleteAppWidgetId(appWidgetId); } + + /** + * Takes a screenshot of the current surface. + */ + public Bitmap takeScreenshot() { + DisplayInfo di = new DisplayInfo(); + mDisplay.getDisplayInfo(di); + return SurfaceControl.screenshot(di.getNaturalWidth(), di.getNaturalHeight()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java new file mode 100644 index 0000000..ad2fa8d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2014 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.recents.views; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import com.android.systemui.recents.Console; +import com.android.systemui.recents.Constants; +import com.android.systemui.recents.RecentsConfiguration; + + +/** + * The full screen transition view that gets animated down from the full screen into a task + * thumbnail view. + */ +public class FullScreenTransitionView extends FrameLayout { + + /** The FullScreenTransitionView callbacks */ + public interface FullScreenTransitionViewCallbacks { + void onEnterAnimationComplete(boolean canceled); + } + + RecentsConfiguration mConfig; + + FullScreenTransitionViewCallbacks mCb; + + ImageView mScreenshotView; + + Rect mClipRect = new Rect(); + + boolean mIsAnimating; + AnimatorSet mEnterAnimation; + + public FullScreenTransitionView(Context context, FullScreenTransitionViewCallbacks cb) { + super(context); + mConfig = RecentsConfiguration.getInstance(); + mCb = cb; + mScreenshotView = new ImageView(context); + mScreenshotView.setScaleType(ImageView.ScaleType.FIT_XY); + mScreenshotView.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + addView(mScreenshotView); + setClipTop(getClipTop()); + setClipBottom(getClipBottom()); + setWillNotDraw(false); + } + + /** Sets the top clip */ + public void setClipTop(int clip) { + mClipRect.top = clip; + postInvalidateOnAnimation(); + } + + /** Gets the top clip */ + public int getClipTop() { + return mClipRect.top; + } + + /** Sets the bottom clip */ + public void setClipBottom(int clip) { + mClipRect.bottom = clip; + postInvalidateOnAnimation(); + } + + /** Gets the top clip */ + public int getClipBottom() { + return mClipRect.bottom; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + } + + @Override + public void draw(Canvas canvas) { + int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG); + canvas.clipRect(mClipRect); + super.draw(canvas); + canvas.restoreToCount(restoreCount); + } + + /** Prepares the screenshot view for the transition into Recents */ + public void prepareAnimateOnEnterRecents(Bitmap screenshot) { + if (!mConfig.launchedFromAppWithScreenshot) return; + + if (Console.Enabled) { + Console.logStartTracingTime(Constants.Log.App.TimeRecentsScreenshotTransition, + Constants.Log.App.TimeRecentsScreenshotTransitionKey); + } + + setClipTop(0); + setClipBottom(getMeasuredHeight()); + setTranslationY(0f); + setScaleX(1f); + setScaleY(1f); + setVisibility(mConfig.launchedFromAppWithScreenshot ? View.VISIBLE : View.INVISIBLE); + if (screenshot != null) { + mScreenshotView.setImageBitmap(screenshot); + } else { + mScreenshotView.setImageDrawable(null); + } + } + + /** Resets the transition view */ + public void reset() { + setVisibility(View.INVISIBLE); + mScreenshotView.setImageDrawable(null); + } + + /** Animates this view as it enters recents */ + public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx, + final Runnable postAnimRunnable) { + if (Console.Enabled) { + Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition, + Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Starting"); + } + + // Cancel the current animation + if (mEnterAnimation != null) { + mEnterAnimation.removeAllListeners(); + mEnterAnimation.cancel(); + } + + // Calculate the bottom clip + float scale = (float) ctx.taskRect.width() / getMeasuredWidth(); + int translationY = -mConfig.systemInsets.top + ctx.stackRectSansPeek.top + + ctx.transform.translationY; + int clipBottom = mConfig.systemInsets.top + (int) (ctx.taskRect.height() / scale); + + // Enable the HW Layers on the screenshot view + mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + + // Compose the animation + mEnterAnimation = new AnimatorSet(); + mEnterAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Notify any callbacks + mCb.onEnterAnimationComplete(false); + // Run the given post-anim runnable + postAnimRunnable.run(); + // Mark that we are no longer animating + mIsAnimating = false; + // Disable the HW Layers on this view + setLayerType(View.LAYER_TYPE_NONE, null); + + if (Console.Enabled) { + Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition, + Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Completed"); + } + } + }); + mEnterAnimation.setStartDelay(0); + mEnterAnimation.setDuration(475); + mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator); + mEnterAnimation.playTogether( + ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top), + ObjectAnimator.ofInt(this, "clipBottom", clipBottom), + ObjectAnimator.ofFloat(this, "translationY", translationY), + ObjectAnimator.ofFloat(this, "scaleX", scale), + ObjectAnimator.ofFloat(this, "scaleY", scale) + ); + mEnterAnimation.start(); + + mIsAnimating = true; + } + + /** Animates this view back out of Recents if we were in the process of animating in. */ + public boolean cancelAnimateOnEnterRecents(final Runnable postAnimRunnable) { + if (mIsAnimating) { + // Cancel the current animation + if (mEnterAnimation != null) { + mEnterAnimation.removeAllListeners(); + mEnterAnimation.cancel(); + } + + // Compose the animation + mEnterAnimation = new AnimatorSet(); + mEnterAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Notify any callbacks + mCb.onEnterAnimationComplete(true); + // Run the given post-anim runnable + postAnimRunnable.run(); + // Mark that we are no longer animating + mIsAnimating = false; + // Disable the HW Layers on the screenshot view + mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null); + } + }); + mEnterAnimation.setDuration(475); + mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator); + mEnterAnimation.playTogether( + ObjectAnimator.ofInt(this, "clipTop", 0), + ObjectAnimator.ofInt(this, "clipBottom", getMeasuredHeight()), + ObjectAnimator.ofFloat(this, "translationY", 0f), + ObjectAnimator.ofFloat(this, "scaleX", 1f), + ObjectAnimator.ofFloat(this, "scaleY", 1f) + ); + mEnterAnimation.start(); + + return true; + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index db398b1..a2c250c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -55,9 +55,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV /** The RecentsView callbacks */ public interface RecentsViewCallbacks { public void onTaskLaunching(boolean isTaskInStackBounds); - public void onEnterAnimationTriggered(); } + RecentsConfiguration mConfig; + LayoutInflater mInflater; + // The space partitioning root of this container SpaceNode mBSP; // Whether there are any tasks @@ -67,10 +69,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // Recents view callbacks RecentsViewCallbacks mCb; - LayoutInflater mInflater; - public RecentsView(Context context) { super(context); + mConfig = RecentsConfiguration.getInstance(); mInflater = LayoutInflater.from(context); setWillNotDraw(false); } @@ -160,18 +161,37 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } /** Requests all task stacks to start their enter-recents animation */ - public void startOnEnterAnimation() { - // Notify callbacks that we are starting the enter animation - mCb.onEnterAnimationTriggered(); - + public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child instanceof TaskStackView) { TaskStackView stackView = (TaskStackView) child; - stackView.startOnEnterAnimation(); + stackView.startOnEnterAnimation(ctx); + } + } + } + + /** Requests all task stacks to start their exit-recents animation */ + public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) { + // Handle the case when there are no views by incrementing and decrementing after all + // animations are started. + ctx.postAnimationTrigger.increment(); + + if (Constants.DebugFlags.App.EnableHomeTransition) { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child instanceof TaskStackView) { + TaskStackView stackView = (TaskStackView) child; + stackView.startOnExitAnimation(ctx); + } } } + + // Handle the case when there are no views by incrementing and decrementing after all + // animations are started. + ctx.postAnimationTrigger.decrement(); } /** Adds the search bar */ @@ -215,10 +235,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } // Get the search bar bounds and measure the search bar layout - RecentsConfiguration config = RecentsConfiguration.getInstance(); if (mSearchBar != null) { Rect searchBarSpaceBounds = new Rect(); - config.getSearchBarBounds(width, height - config.systemInsets.top, searchBarSpaceBounds); + mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds); mSearchBar.measure( MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY)); @@ -229,9 +248,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // In addition, we give it the full height, not including the top inset or search bar space, // since we want the tasks to render under the navigation buttons in portrait. Rect taskStackBounds = new Rect(); - config.getTaskStackBounds(width, height, taskStackBounds); - int childWidth = width - config.systemInsets.right; - int childHeight = taskStackBounds.height() - config.systemInsets.top; + mConfig.getTaskStackBounds(width, height, taskStackBounds); + int childWidth = width - mConfig.systemInsets.right; + int childHeight = taskStackBounds.height() - mConfig.systemInsets.top; // Measure each TaskStackView int childCount = getChildCount(); @@ -259,23 +278,22 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } // Get the search bar bounds so that we lay it out - RecentsConfiguration config = RecentsConfiguration.getInstance(); if (mSearchBar != null) { Rect searchBarSpaceBounds = new Rect(); - config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds); - mSearchBar.layout(config.systemInsets.left + searchBarSpaceBounds.left, - config.systemInsets.top + searchBarSpaceBounds.top, - config.systemInsets.left + mSearchBar.getMeasuredWidth(), - config.systemInsets.top + mSearchBar.getMeasuredHeight()); + mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds); + mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left, + mConfig.systemInsets.top + searchBarSpaceBounds.top, + mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(), + mConfig.systemInsets.top + mSearchBar.getMeasuredHeight()); } // We offset the stack view by the left inset (if any), but lay it out under the search bar. // In addition, we offset our stack views by the top inset and search bar height, but not // the bottom insets because we want it to render under the navigation buttons. Rect taskStackBounds = new Rect(); - config.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds); - left += config.systemInsets.left; - top += config.systemInsets.top + taskStackBounds.top; + mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds); + left += mConfig.systemInsets.left; + top += mConfig.systemInsets.top + taskStackBounds.top; // Layout each child // XXX: Based on the space node for that task view @@ -324,8 +342,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } // Update the configuration with the latest system insets and trigger a relayout - RecentsConfiguration config = RecentsConfiguration.getInstance(); - config.updateSystemInsets(insets.getSystemWindowInsets()); + mConfig.updateSystemInsets(insets.getSystemWindowInsets()); requestLayout(); return insets.consumeSystemWindowInsets(false, false, false, true); @@ -365,6 +382,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV final Runnable launchRunnable = new Runnable() { @Override public void run() { + if (Console.Enabled) { + Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, + Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity"); + } + TaskViewTransform transform; View sourceView = tv; int offsetX = 0; @@ -374,11 +396,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // If there is no actual task view, then use the stack view as the source view // and then offset to the expected transform rect, but bound this to just // outside the display rect (to ensure we don't animate from too far away) - RecentsConfiguration config = RecentsConfiguration.getInstance(); sourceView = stackView; transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll); offsetX = transform.rect.left; - offsetY = Math.min(transform.rect.top, config.displayRect.height()); + offsetY = Math.min(transform.rect.top, mConfig.displayRect.height()); } else { transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll); } @@ -426,19 +447,23 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV onTaskRemoved(task); } - Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey, "startActivity"); + if (Console.Enabled) { + Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, + Constants.Log.App.TimeRecentsLaunchKey, "startActivity"); + } } }; - Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched"); + if (Console.Enabled) { + Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, + Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched"); + } // Launch the app right away if there is no task view, otherwise, animate the icon out first if (tv == null) { post(launchRunnable); } else { - tv.animateOnLeavingRecents(launchRunnable); + tv.animateOnLaunchingTask(launchRunnable); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java index c10ddd1..2c637a8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java @@ -22,8 +22,10 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -36,6 +38,9 @@ import com.android.systemui.recents.model.Task; /* The task bar view */ class TaskBarView extends FrameLayout { + + RecentsConfiguration mConfig; + Task mTask; ImageView mDismissButton; @@ -61,6 +66,7 @@ class TaskBarView extends FrameLayout { public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mConfig = RecentsConfiguration.getInstance(); setWillNotDraw(false); // Load the dismiss resources @@ -70,11 +76,10 @@ class TaskBarView extends FrameLayout { // Configure the highlight paint if (sHighlightPaint == null) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); sHighlightPaint = new Paint(); sHighlightPaint.setStyle(Paint.Style.STROKE); - sHighlightPaint.setStrokeWidth(config.taskViewHighlightPx); - sHighlightPaint.setColor(config.taskBarViewHighlightColor); + sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx); + sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor); sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); sHighlightPaint.setAntiAlias(true); } @@ -90,11 +95,9 @@ class TaskBarView extends FrameLayout { @Override protected void onDraw(Canvas canvas) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - // Draw the highlight at the top edge (but put the bottom edge just out of view) - float offset = config.taskViewHighlightPx / 2f; - float radius = config.taskViewRoundedCornerRadiusPx; + float offset = mConfig.taskViewHighlightPx / 2f; + float radius = mConfig.taskViewRoundedCornerRadiusPx; canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset, getMeasuredHeight() + radius, radius, radius, sHighlightPaint); } @@ -102,7 +105,6 @@ class TaskBarView extends FrameLayout { /** Synchronizes this bar view's properties with the task's transform */ void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform, TaskViewTransform toTransform, int duration) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); if (duration > 0) { if (animateFromTransform != null) { mDismissButton.setAlpha(animateFromTransform.dismissAlpha); @@ -111,18 +113,16 @@ class TaskBarView extends FrameLayout { .alpha(toTransform.dismissAlpha) .setStartDelay(0) .setDuration(duration) - .setInterpolator(config.fastOutSlowInInterpolator) + .setInterpolator(mConfig.fastOutSlowInInterpolator) .withLayer() .start(); } else { mDismissButton.setAlpha(toTransform.dismissAlpha); } - mDismissButton.invalidate(); } /** Binds the bar view to the task */ void rebindToTask(Task t, boolean animate) { - RecentsConfiguration configuration = RecentsConfiguration.getInstance(); mTask = t; // If an activity icon is defined, then we use that as the primary icon to show in the bar, // otherwise, we fall back to the application icon @@ -137,12 +137,12 @@ class TaskBarView extends FrameLayout { if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) { setBackgroundColor(tint); mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint, - configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor)); + mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor)); mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint, mLightDismissDrawable, mDarkDismissDrawable)); } else { - setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor); - mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor); + setBackgroundColor(mConfig.taskBarViewDefaultBackgroundColor); + mActivityDescription.setTextColor(mConfig.taskBarViewDefaultTextColor); } if (animate) { // XXX: Investigate how expensive it will be to create a second bitmap and crossfade @@ -155,4 +155,51 @@ class TaskBarView extends FrameLayout { mApplicationIcon.setImageDrawable(null); mActivityDescription.setText(""); } + + /** Prepares this task view for the enter-recents animations. This is called earlier in the + * first layout because the actual animation into recents may take a long time. */ + public void prepareAnimateEnterRecents() { + setVisibility(View.INVISIBLE); + } + + /** Animates this task bar as it enters recents */ + public void animateOnEnterRecents(int delay) { + // Animate the task bar of the first task view + setVisibility(View.VISIBLE); + setTranslationY(-getMeasuredHeight()); + animate() + .translationY(0) + .setStartDelay(delay > -1 ? delay : mConfig.taskBarEnterAnimDelay) + .setInterpolator(mConfig.fastOutSlowInInterpolator) + .setDuration(mConfig.taskBarEnterAnimDuration) + .withLayer() + .start(); + } + + /** Animates this task bar as it exits recents */ + public void animateOnLaunchingTask(final Runnable r) { + animate() + .translationY(-getMeasuredHeight()) + .setStartDelay(0) + .setInterpolator(mConfig.fastOutLinearInInterpolator) + .setDuration(mConfig.taskBarExitAnimDuration) + .withLayer() + .withEndAction(new Runnable() { + @Override + public void run() { + post(r); + } + }) + .start(); + } + + /** Enable the hw layers on this task view */ + void enableHwLayers() { + mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + + /** Disable the hw layers on this task view */ + void disableHwLayers() { + mDismissButton.setLayerType(View.LAYER_TYPE_NONE, null); + } } 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 5830e37..186565b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -41,6 +41,7 @@ import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.RecentsPackageMonitor; import com.android.systemui.recents.RecentsTaskLoader; +import com.android.systemui.recents.ReferenceCountedTrigger; import com.android.systemui.recents.Utilities; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; @@ -62,10 +63,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal public void onTaskRemoved(Task t); } + RecentsConfiguration mConfig; + TaskStack mStack; TaskStackViewTouchHandler mTouchHandler; TaskStackViewCallbacks mCb; ViewPool<TaskView, Task> mViewPool; + ArrayList<TaskViewTransform> mTaskTransforms = new ArrayList<TaskViewTransform>(); // The various rects that define the stack view Rect mRect = new Rect(); @@ -83,11 +87,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal ObjectAnimator mScrollAnimator; // Optimizations - int mHwLayersRefCount; + ReferenceCountedTrigger mHwLayersTrigger; int mStackViewsAnimationDuration; boolean mStackViewsDirty = true; boolean mAwaitingFirstLayout = true; boolean mStartEnterAnimationRequestedAfterLayout; + ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext; int[] mTmpVisibleRange = new int[2]; Rect mTmpRect = new Rect(); Rect mTmpRect2 = new Rect(); @@ -95,12 +100,40 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal public TaskStackView(Context context, TaskStack stack) { super(context); + mConfig = RecentsConfiguration.getInstance(); mStack = stack; mStack.setCallbacks(this); mScroller = new OverScroller(context); mTouchHandler = new TaskStackViewTouchHandler(context, this); mViewPool = new ViewPool<TaskView, Task>(context, this); mInflater = LayoutInflater.from(context); + mHwLayersTrigger = new ReferenceCountedTrigger(getContext(), new Runnable() { + @Override + public void run() { + // Enable hw layers on each of the children + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + TaskView tv = (TaskView) getChildAt(i); + tv.enableHwLayers(); + } + } + }, new Runnable() { + @Override + public void run() { + // Disable hw layers on each of the children + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + TaskView tv = (TaskView) getChildAt(i); + tv.disableHwLayers(); + } + } + }, new Runnable() { + @Override + public void run() { + new Throwable("Invalid hw layers ref count").printStackTrace(); + Console.logError(getContext(), "Invalid HW layers ref count"); + } + }); } /** Sets the callbacks */ @@ -118,7 +151,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal "[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow); } if (!mStackViewsDirty) { - invalidate(); + invalidate(mStackRect); } if (mAwaitingFirstLayout) { // Skip the animation if we are awaiting first layout @@ -156,14 +189,16 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal float boundedT = Math.max(t, -(numPeekCards + 1)); // Set the scale relative to its position + int numFrontScaledCards = 3; float minScale = Constants.Values.TaskStackView.StackPeekMinScale; float scaleRange = 1f - minScale; - float scaleInc = scaleRange / numPeekCards; - float scale = Math.max(minScale, Math.min(1f, 1f + (boundedT * scaleInc))); + float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards); + float scale = Math.max(minScale, Math.min(1f, minScale + + ((boundedT + (numPeekCards + 1)) * scaleInc))); float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2; transform.scale = scale; - // Set the translation + // Set the y translation if (boundedT < 0f) { transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) / numPeekCards) * peekHeight - scaleYOffset); @@ -171,6 +206,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset); } + // Set the z translation + int minZ = mConfig.taskViewTranslationZMinPx; + int incZ = mConfig.taskViewTranslationZIncrementPx; + transform.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ)); + // Set the alphas transform.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f; @@ -190,16 +230,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** * Gets the stack transforms of a list of tasks, and returns the visible range of tasks. */ - private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks, - int stackScroll, - int[] visibleRangeOut, - boolean boundTranslationsToRect) { + private void updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms, + ArrayList<Task> tasks, + int stackScroll, + int[] visibleRangeOut, + boolean boundTranslationsToRect) { // XXX: Optimization: Use binary search to find the visible range - ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>(); int taskCount = tasks.size(); int firstVisibleIndex = -1; int lastVisibleIndex = -1; + taskTransforms.clear(); + taskTransforms.ensureCapacity(taskCount); for (int i = 0; i < taskCount; i++) { TaskViewTransform transform = getStackTransform(i, stackScroll); taskTransforms.add(transform); @@ -218,6 +260,19 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal visibleRangeOut[0] = firstVisibleIndex; visibleRangeOut[1] = lastVisibleIndex; } + } + + /** + * Gets the stack transforms of a list of tasks, and returns the visible range of tasks. This + * call is less optimal than calling updateStackTransforms directly. + */ + private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks, + int stackScroll, + int[] visibleRangeOut, + boolean boundTranslationsToRect) { + ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>(); + updateStackTransforms(taskTransforms, tasks, stackScroll, visibleRangeOut, + boundTranslationsToRect); return taskTransforms; } @@ -237,14 +292,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int[] visibleRange = mTmpVisibleRange; int stackScroll = getStackScroll(); ArrayList<Task> tasks = mStack.getTasks(); - ArrayList<TaskViewTransform> taskTransforms = getStackTransforms(tasks, stackScroll, - visibleRange, false); + updateStackTransforms(mTaskTransforms, tasks, stackScroll, visibleRange, false); // Update the visible state of all the tasks int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { Task task = tasks.get(i); - TaskViewTransform transform = taskTransforms.get(i); + TaskViewTransform transform = mTaskTransforms.get(i); TaskView tv = getChildViewForTask(task); if (transform.visible) { @@ -273,10 +327,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskView tv = (TaskView) getChildAt(i); Task task = tv.getTask(); int taskIndex = mStack.indexOfTask(task); - if (taskIndex < 0 || !taskTransforms.get(taskIndex).visible) { + if (taskIndex < 0 || !mTaskTransforms.get(taskIndex).visible) { mViewPool.returnViewToPool(tv); } else { - tv.updateViewPropertiesToTaskTransform(null, taskTransforms.get(taskIndex), + tv.updateViewPropertiesToTaskTransform(null, mTaskTransforms.get(taskIndex), mStackViewsAnimationDuration); } } @@ -353,7 +407,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll); mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll - curScroll, 250)); - mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().fastOutSlowInInterpolator); + mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator); mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { @@ -535,48 +589,31 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** Enables the hw layers and increments the hw layer requirement ref count */ void addHwLayersRefCount(String reason) { if (Console.Enabled) { + int refCount = mHwLayersTrigger.getCount(); Console.log(Constants.Log.UI.HwLayers, "[TaskStackView|addHwLayersRefCount] refCount: " + - mHwLayersRefCount + "->" + (mHwLayersRefCount + 1) + " " + reason); + refCount + "->" + (refCount + 1) + " " + reason); } - if (mHwLayersRefCount == 0) { - // Enable hw layers on each of the children - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - TaskView tv = (TaskView) getChildAt(i); - tv.enableHwLayers(); - } - } - mHwLayersRefCount++; + mHwLayersTrigger.increment(); } /** Decrements the hw layer requirement ref count and disables the hw layers when we don't need them anymore. */ void decHwLayersRefCount(String reason) { if (Console.Enabled) { + int refCount = mHwLayersTrigger.getCount(); Console.log(Constants.Log.UI.HwLayers, "[TaskStackView|decHwLayersRefCount] refCount: " + - mHwLayersRefCount + "->" + (mHwLayersRefCount - 1) + " " + reason); - } - mHwLayersRefCount--; - if (mHwLayersRefCount == 0) { - // Disable hw layers on each of the children - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - TaskView tv = (TaskView) getChildAt(i); - tv.disableHwLayers(); - } - } else if (mHwLayersRefCount < 0) { - new Throwable("Invalid hw layers ref count").printStackTrace(); - Console.logError(getContext(), "Invalid HW layers ref count"); + refCount + "->" + (refCount - 1) + " " + reason); } + mHwLayersTrigger.decrement(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { setStackScroll(mScroller.getCurrY()); - invalidate(); + invalidate(mStackRect); // If we just finished scrolling, then disable the hw layers if (mScroller.isFinished()) { @@ -608,7 +645,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (Constants.DebugFlags.App.EnableTaskStackClipping) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); TaskView tv = (TaskView) child; TaskView nextTv = null; TaskView tmpTv = null; @@ -624,13 +660,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Clip against the next view (if we aren't animating its alpha) - if (nextTv != null && nextTv.getAlpha() == 1f) { + if (nextTv != null) { Rect curRect = tv.getClippingRect(mTmpRect); Rect nextRect = nextTv.getClippingRect(mTmpRect2); // The hit rects are relative to the task view, which needs to be offset by // the system bar height - curRect.offset(0, config.systemInsets.top); - nextRect.offset(0, config.systemInsets.top); + curRect.offset(0, mConfig.systemInsets.top); + nextRect.offset(0, mConfig.systemInsets.top); // Compute the clip region Region clipRegion = new Region(); clipRegion.op(curRect, Region.Op.UNION); @@ -652,7 +688,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Note: We let the stack view be the full height because we want the cards to go under the // navigation bar if possible. However, the stack rects which we use to calculate // max scroll, etc. need to take the nav bar into account - RecentsConfiguration config = RecentsConfiguration.getInstance(); // Compute the stack rects mRect.set(0, 0, width, height); @@ -660,8 +695,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mStackRect.left += insetLeft; mStackRect.bottom -= insetBottom; - int widthPadding = (int) (config.taskStackWidthPaddingPct * mStackRect.width()); - int heightPadding = config.taskStackTopPaddingPx; + int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width()); + int heightPadding = mConfig.taskStackTopPaddingPx; if (Constants.DebugFlags.App.EnableSearchLayout) { mStackRect.top += heightPadding; mStackRect.left += widthPadding; @@ -699,10 +734,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Compute our stack/task rects - RecentsConfiguration config = RecentsConfiguration.getInstance(); Rect taskStackBounds = new Rect(); - config.getTaskStackBounds(width, height, taskStackBounds); - computeRects(width, height, taskStackBounds.left, config.systemInsets.bottom); + mConfig.getTaskStackBounds(width, height, taskStackBounds); + computeRects(width, height, taskStackBounds.left, mConfig.systemInsets.bottom); // Debug logging if (Constants.Log.UI.MeasureAndLayout) { @@ -760,47 +794,66 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } if (mAwaitingFirstLayout) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - - // Update the focused task index to be the next item to the top task - if (config.launchedFromAltTab) { - focusTask(Math.max(0, mStack.getTaskCount() - 2), false); - } + // Mark that we have completely the first layout + mAwaitingFirstLayout = false; // Prepare the first view for its enter animation - if (config.launchedWithThumbnailAnimation) { - TaskView tv = (TaskView) getChildAt(getChildCount() - 1); - if (tv != null) { - tv.prepareAnimateOnEnterRecents(); - } + int offsetTopAlign = -mTaskRect.top; + int offscreenY = mRect.bottom - (mTaskRect.top - mRect.top); + for (int i = childCount - 1; i >= 0; i--) { + TaskView tv = (TaskView) getChildAt(i); + tv.prepareAnimateEnterRecents((i == (getChildCount() - 1)), offsetTopAlign, + offscreenY, mTaskRect); } - // Mark that we have completely the first layout - mAwaitingFirstLayout = false; - // If the enter animation started already and we haven't completed a layout yet, do the // enter animation now if (mStartEnterAnimationRequestedAfterLayout) { - startOnEnterAnimation(); + startOnEnterAnimation(mStartEnterAnimationContext); + mStartEnterAnimationRequestedAfterLayout = false; + mStartEnterAnimationContext = null; + } + + // Update the focused task index to be the next item to the top task + if (mConfig.launchedWithAltTab) { + focusTask(Math.max(0, mStack.getTaskCount() - 2), false); } } } /** Requests this task stacks to start it's enter-recents animation */ - public void startOnEnterAnimation() { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - if (!config.launchedWithThumbnailAnimation) return; - + public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) { // If we are still waiting to layout, then just defer until then if (mAwaitingFirstLayout) { mStartEnterAnimationRequestedAfterLayout = true; + mStartEnterAnimationContext = ctx; return; } - // Animate the task bar of the first task view - TaskView tv = (TaskView) getChildAt(getChildCount() - 1); - if (tv != null) { - tv.animateOnEnterRecents(); + // Animate all the task views into view + ctx.taskRect = mTaskRect; + ctx.stackRectSansPeek = mStackRectSansPeek; + int childCount = getChildCount(); + for (int i = childCount - 1; i >= 0; i--) { + TaskView tv = (TaskView) getChildAt(i); + TaskViewTransform transform = getStackTransform(mStack.indexOfTask(tv.getTask()), + getStackScroll()); + ctx.stackViewIndex = i; + ctx.stackViewCount = childCount; + ctx.isFrontMost = (i == (getChildCount() - 1)); + ctx.transform = transform; + tv.animateOnEnterRecents(ctx); + } + } + + /** Requests this task stacks to start it's exit-recents animation. */ + public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) { + // Animate all the task views into view + ctx.offscreenTranslationY = mRect.bottom - (mTaskRect.top - mRect.top); + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + TaskView tv = (TaskView) getChildAt(i); + tv.animateOnExitRecents(ctx); } } @@ -863,8 +916,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal ArrayList<TaskViewTransform> curTaskTransforms, ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms, HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut, - ArrayList<TaskView> childrenToRemoveOut, - RecentsConfiguration config) { + ArrayList<TaskView> childrenToRemoveOut) { // Animate all of the existing views out of view (if they are not in the visible range in // the new stack) or to their final positions in the new stack int movement = 0; @@ -894,7 +946,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal childViewTransformsOut.put(tv, new Pair(0, toTransform)); } return Utilities.calculateTranslationAnimationDuration(movement, - config.filteringCurrentViewsMinAnimDuration); + mConfig.filteringCurrentViewsMinAnimDuration); } /** @@ -903,8 +955,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms, - HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut, - RecentsConfiguration config) { + HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut) { int offset = 0; int movement = 0; int taskCount = tasks.size(); @@ -934,7 +985,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } return Utilities.calculateTranslationAnimationDuration(movement, - config.filteringNewViewsMinAnimDuration); + mConfig.filteringNewViewsMinAnimDuration); } /** Orchestrates the animations of the current child views and any new views. */ @@ -942,22 +993,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal ArrayList<TaskViewTransform> curTaskTransforms, final ArrayList<Task> tasks, final ArrayList<TaskViewTransform> taskTransforms) { - final RecentsConfiguration config = RecentsConfiguration.getInstance(); - // Calculate the transforms to animate out all the existing views if they are not in the // new visible range (or to their final positions in the stack if they are) final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>(); final HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransforms = new HashMap<TaskView, Pair<Integer, TaskViewTransform>>(); int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks, - taskTransforms, childViewTransforms, childrenToRemove, config); + taskTransforms, childViewTransforms, childrenToRemove); // If all the current views are in the visible range of the new stack, then don't wait for // views to animate out and animate all the new views into their place final boolean unifyNewViewAnimation = childrenToRemove.isEmpty(); if (unifyNewViewAnimation) { int inDuration = getEnterTransformsForFilterAnimation(tasks, taskTransforms, - childViewTransforms, config); + childViewTransforms); duration = Math.max(duration, inDuration); } @@ -981,7 +1030,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // For views that are not already visible, animate them in childViewTransforms.clear(); int duration = getEnterTransformsForFilterAnimation(tasks, - taskTransforms, childViewTransforms, config); + taskTransforms, childViewTransforms); for (final TaskView tv : childViewTransforms.keySet()) { Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv); tv.animate().setStartDelay(t.first); @@ -1119,7 +1168,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Enable hw layers on this view if hw layers are enabled on the stack - if (mHwLayersRefCount > 0) { + if (mHwLayersTrigger.getCount() > 0) { tv.enableHwLayers(); } } @@ -1188,7 +1237,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void onComponentRemoved(Set<ComponentName> cns) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); // For other tasks, just remove them directly if they no longer exist ArrayList<Task> tasks = mStack.getTasks(); for (int i = tasks.size() - 1; i >= 0; i--) { @@ -1494,7 +1542,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { mSv.mMinScroll, mSv.mMaxScroll, 0, overscrollRange); // Invalidate to kick off computeScroll - mSv.invalidate(); + mSv.invalidate(mSv.mStackRect); } else if (mSv.isScrollOutOfBounds()) { // Animate the scroll back into bounds // XXX: Make this animation a function of the velocity OR distance diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 632c816..9e4386f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -28,9 +28,11 @@ import android.graphics.RectF; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; +import android.view.ViewParent; import android.view.animation.AccelerateInterpolator; import android.widget.FrameLayout; import com.android.systemui.R; +import com.android.systemui.recents.Console; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.model.Task; @@ -45,10 +47,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On public void onTaskAppInfoClicked(TaskView tv); public void onTaskFocused(TaskView tv); public void onTaskDismissed(TaskView tv); - - // public void onTaskViewReboundToTask(TaskView tv, Task t); } + RecentsConfiguration mConfig; + int mDim; int mMaxDim; TimeInterpolator mDimInterpolator = new AccelerateInterpolator(); @@ -59,11 +61,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On boolean mClipViewInStack; Point mLastTouchDown = new Point(); Path mRoundedRectClipPath = new Path(); + Rect mTmpRect = new Rect(); TaskThumbnailView mThumbnailView; TaskBarView mBarView; TaskViewCallbacks mCb; + // Optimizations + ValueAnimator.AnimatorUpdateListener mUpdateDimListener = + new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + updateDimOverlayFromScale(); + } + }; + public TaskView(Context context) { this(context, null); @@ -79,13 +91,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mConfig = RecentsConfiguration.getInstance(); setWillNotDraw(false); } @Override protected void onFinishInflate() { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - mMaxDim = config.taskStackMaxDim; + mMaxDim = mConfig.taskStackMaxDim; // By default, all views are clipped to other views in their stack mClipViewInStack = true; @@ -104,8 +116,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Update the rounded rect clip path - RecentsConfiguration config = RecentsConfiguration.getInstance(); - float radius = config.taskViewRoundedCornerRadiusPx; + float radius = mConfig.taskViewRoundedCornerRadiusPx; mRoundedRectClipPath.reset(); mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), radius, radius, Path.Direction.CW); @@ -113,7 +124,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On // Update the outline Outline o = new Outline(); o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() - - config.taskViewShadowOutlineBottomInsetPx, radius); + mConfig.taskViewShadowOutlineBottomInsetPx, radius); setOutline(o); } @@ -141,9 +152,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On /** Synchronizes this view's properties with the task's transform */ void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform, TaskViewTransform toTransform, int duration) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - int minZ = config.taskViewTranslationZMinPx; - int incZ = config.taskViewTranslationZIncrementPx; + if (Console.Enabled) { + Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]", + "duration: " + duration, Console.AnsiPurple); + } // Update the bar view mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration); @@ -153,33 +165,28 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On if (animateFromTransform != null) { setTranslationY(animateFromTransform.translationY); if (Constants.DebugFlags.App.EnableShadows) { - setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ))); + setTranslationZ(animateFromTransform.translationZ); } setScaleX(animateFromTransform.scale); setScaleY(animateFromTransform.scale); setAlpha(animateFromTransform.alpha); } if (Constants.DebugFlags.App.EnableShadows) { - animate().translationZ(Math.max(minZ, minZ + (toTransform.t * incZ))); + animate().translationZ(toTransform.translationZ); } animate().translationY(toTransform.translationY) .scaleX(toTransform.scale) .scaleY(toTransform.scale) .alpha(toTransform.alpha) + .setStartDelay(0) .setDuration(duration) - .setInterpolator(config.fastOutSlowInInterpolator) - .withLayer() - .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - updateDimOverlayFromScale(); - } - }) + .setInterpolator(mConfig.fastOutSlowInInterpolator) + .setUpdateListener(mUpdateDimListener) .start(); } else { setTranslationY(toTransform.translationY); if (Constants.DebugFlags.App.EnableShadows) { - setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ))); + setTranslationZ(toTransform.translationZ); } setScaleX(toTransform.scale); setScaleY(toTransform.scale); @@ -199,6 +206,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On setScaleX(1f); setScaleY(1f); setAlpha(1f); + mDim = 0; invalidate(); } @@ -223,40 +231,103 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On /** Prepares this task view for the enter-recents animations. This is called earlier in the * first layout because the actual animation into recents may take a long time. */ - public void prepareAnimateOnEnterRecents() { - mBarView.setVisibility(View.INVISIBLE); + public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY, + Rect taskRect) { + if (mConfig.launchedFromAppWithScreenshot) { + if (isTaskViewFrontMost) { + // Hide the task view as we are going to animate the full screenshot into view + // and then replace it with this view once we are done + setVisibility(View.INVISIBLE); + // Also hide the front most task bar view so we can animate it in + mBarView.prepareAnimateEnterRecents(); + } else { + // Top align the task views + setTranslationY(offsetY); + setScaleX(1f); + setScaleY(1f); + } + + } else if (mConfig.launchedFromAppWithThumbnail) { + if (isTaskViewFrontMost) { + // Hide the front most task bar view so we can animate it in + mBarView.prepareAnimateEnterRecents(); + } + + } else if (mConfig.launchedFromHome) { + // Move the task view off screen (below) so we can animate it in + setTranslationY(offscreenY); + setScaleX(1f); + setScaleY(1f); + } } /** Animates this task view as it enters recents */ - public void animateOnEnterRecents() { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - mBarView.setVisibility(View.VISIBLE); - mBarView.setTranslationY(-mBarView.getMeasuredHeight()); - mBarView.animate() - .translationY(0) - .setStartDelay(config.taskBarEnterAnimDelay) - .setInterpolator(config.fastOutSlowInInterpolator) - .setDuration(config.taskBarEnterAnimDuration) + public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) { + TaskViewTransform transform = ctx.transform; + + if (mConfig.launchedFromAppWithScreenshot) { + if (ctx.isFrontMost) { + // Animate the full screenshot down first, before swapping with this task view + ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() { + @Override + public void run() { + // Animate the task bar of the first task view + mBarView.animateOnEnterRecents(0); + setVisibility(View.VISIBLE); + } + }); + } else { + // Animate the tasks down behind the full screenshot + animate() + .scaleX(transform.scale) + .scaleY(transform.scale) + .translationY(transform.translationY) + .setStartDelay(0) + .setInterpolator(mConfig.linearOutSlowInInterpolator) + .setDuration(475) + .withLayer() + .start(); + } + + } else if (mConfig.launchedFromAppWithThumbnail) { + if (ctx.isFrontMost) { + // Animate the task bar of the first task view + mBarView.animateOnEnterRecents(-1); + } + + } else if (mConfig.launchedFromHome) { + // Animate the tasks up + int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1); + int delay = mConfig.taskBarEnterAnimDelay + + frontIndex * mConfig.taskViewEnterFromHomeDelay; + animate() + .scaleX(transform.scale) + .scaleY(transform.scale) + .translationY(transform.translationY) + .setStartDelay(delay) + .setInterpolator(mConfig.quintOutInterpolator) + .setDuration(mConfig.taskViewEnterFromHomeDuration) + .withLayer() + .start(); + } + } + + /** Animates this task view as it leaves recents */ + public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) { + animate() + .translationY(ctx.offscreenTranslationY) + .setStartDelay(0) + .setInterpolator(mConfig.fastOutSlowInInterpolator) + .setDuration(mConfig.taskViewEnterFromHomeDuration) .withLayer() + .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable()) .start(); + ctx.postAnimationTrigger.increment(); } /** Animates this task view as it exits recents */ - public void animateOnLeavingRecents(final Runnable r) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - mBarView.animate() - .translationY(-mBarView.getMeasuredHeight()) - .setStartDelay(0) - .setInterpolator(config.fastOutLinearInInterpolator) - .setDuration(config.taskBarExitAnimDuration) - .withLayer() - .withEndAction(new Runnable() { - @Override - public void run() { - post(r); - } - }) - .start(); + public void animateOnLaunchingTask(final Runnable r) { + mBarView.animateOnLaunchingTask(r); } /** Animates the deletion of this task view */ @@ -264,20 +335,24 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On // Disabling clipping with the stack while the view is animating away setClipViewInStack(false); - RecentsConfiguration config = RecentsConfiguration.getInstance(); - animate().translationX(config.taskViewRemoveAnimTranslationXPx) + animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx) .alpha(0f) .setStartDelay(0) - .setInterpolator(config.fastOutSlowInInterpolator) - .setDuration(config.taskViewRemoveAnimDuration) + .setInterpolator(mConfig.fastOutSlowInInterpolator) + .setDuration(mConfig.taskViewRemoveAnimDuration) .withLayer() .withEndAction(new Runnable() { @Override public void run() { - post(r); + // We just throw this into a runnable because starting a view property + // animation using layers can cause inconsisten results if we try and + // update the layers while the animation is running. In some cases, + // the runnabled passed in may start an animation which also uses layers + // so we defer all this by posting this. + r.run(); // Re-enable clipping with the stack (we will reuse this view) - setClipViewInStack(false); + setClipViewInStack(true); } }) .start(); @@ -295,11 +370,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On /** Enable the hw layers on this task view */ void enableHwLayers() { mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mBarView.enableHwLayers(); } /** Disable the hw layers on this task view */ void disableHwLayers() { mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null); + mBarView.disableHwLayers(); } /** @@ -307,7 +384,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On * view. */ boolean shouldClipViewInStack() { - return mClipViewInStack; + return mClipViewInStack && (getVisibility() == View.VISIBLE); } /** Sets whether this view should be clipped, or clipped against. */ @@ -315,9 +392,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On if (clip != mClipViewInStack) { mClipViewInStack = clip; if (getParent() instanceof View) { - Rect r = new Rect(); - getHitRect(r); - ((View) getParent()).invalidate(r); + getHitRect(mTmpRect); + ((View) getParent()).invalidate(mTmpRect); } } } @@ -393,8 +469,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On mBarView.mApplicationIcon.setOnClickListener(this); mBarView.mDismissButton.setOnClickListener(this); if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) { - RecentsConfiguration config = RecentsConfiguration.getInstance(); - if (config.developerOptionsEnabled) { + if (mConfig.developerOptionsEnabled) { mBarView.mApplicationIcon.setOnLongClickListener(this); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java index e6391a8..4a76872 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java @@ -22,6 +22,7 @@ import android.graphics.Rect; /* The transform state for a task view */ public class TaskViewTransform { public int translationY = 0; + public int translationZ = 0; public float scale = 1f; public float alpha = 1f; public float dismissAlpha = 1f; @@ -35,6 +36,7 @@ public class TaskViewTransform { public TaskViewTransform(TaskViewTransform o) { translationY = o.translationY; + translationZ = o.translationZ; scale = o.scale; alpha = o.alpha; dismissAlpha = o.dismissAlpha; @@ -45,7 +47,8 @@ public class TaskViewTransform { @Override public String toString() { - return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha + - " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha; + return "TaskViewTransform y: " + translationY + " z: " + translationZ + " scale: " + scale + + " alpha: " + alpha + " visible: " + visible + " rect: " + rect + + " dismissAlpha: " + dismissAlpha; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java new file mode 100644 index 0000000..b5e8ffd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 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.recents.views; + +import android.graphics.Rect; +import com.android.systemui.recents.ReferenceCountedTrigger; + +/* Common code related to view animations */ +public class ViewAnimation { + + /* The animation context for a task view animation into Recents */ + public static class TaskViewEnterContext { + // The full screenshot view that we are animating down + FullScreenTransitionView fullScreenshot; + // The transform of the current task view + TaskViewTransform transform; + // The stack rect that the transform is relative to + Rect stackRectSansPeek; + // The task rect + Rect taskRect; + // The view index of the current task view + int stackViewIndex; + // The total number of task views + int stackViewCount; + // Whether this is the front most task view + boolean isFrontMost; + + public TaskViewEnterContext(FullScreenTransitionView fss) { + fullScreenshot = fss; + } + } + + /* The animation context for a task view animation out of Recents */ + public static class TaskViewExitContext { + // A trigger to run some logic when all the animations complete. This works around the fact + // that it is difficult to coordinate ViewPropertyAnimators + ReferenceCountedTrigger postAnimationTrigger; + // The translationY to apply to a TaskView to move it off screen + int offscreenTranslationY; + + public TaskViewExitContext(ReferenceCountedTrigger t) { + postAnimationTrigger = t; + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index a770f58..80e85f9 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -34,6 +34,7 @@ import java.util.ArrayList; public class BrightnessController implements ToggleSlider.Listener { private static final String TAG = "StatusBar.BrightnessController"; + private static final boolean SHOW_AUTOMATIC_ICON = false; /** * {@link android.provider.Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ} uses the range [-1, 1]. @@ -232,7 +233,7 @@ public class BrightnessController implements ToggleSlider.Listener { private void updateIcon(boolean automatic) { if (mIcon != null) { - mIcon.setImageResource(automatic ? + mIcon.setImageResource(automatic && SHOW_AUTOMATIC_ICON ? com.android.systemui.R.drawable.ic_qs_brightness_auto_on : com.android.systemui.R.drawable.ic_qs_brightness_auto_off); } diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java index d67e7cb..a3b10f2 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java +++ b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java @@ -21,8 +21,16 @@ import com.android.systemui.R; import android.app.ActivityManagerNative; import android.content.Context; import android.content.pm.UserInfo; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Shader; +import android.os.Handler; import android.os.RemoteException; import android.os.UserManager; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; @@ -43,15 +51,18 @@ import java.util.List; /** * A quick and dirty view to show a user switcher. */ -public class UserSwitcherHostView extends FrameLayout implements ListView.OnItemClickListener { +public class UserSwitcherHostView extends FrameLayout + implements ListView.OnItemClickListener, View.OnClickListener { private static final String TAG = "UserSwitcherDialog"; private ArrayList<UserInfo> mUserInfo = new ArrayList<UserInfo>(); + private UserInfo mGuestUser; private Adapter mAdapter = new Adapter(); private UserManager mUserManager; private Runnable mFinishRunnable; private ListView mListView; + private boolean mGuestUserEnabled; public UserSwitcherHostView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); @@ -60,6 +71,9 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem return; } mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + + mGuestUserEnabled = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.GUEST_USER_ENABLED, 0) == 1; } public UserSwitcherHostView(Context context, AttributeSet attrs) { @@ -80,7 +94,39 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem @Override public void onItemClick(AdapterView<?> l, View v, int position, long id) { - int userId = mAdapter.getItem(position).id; + // Last item is the guest + if (position == mUserInfo.size()) { + postDelayed(new Runnable() { + public void run() { + switchToGuestUser(); + } + }, 100); + } else { + final int userId = mAdapter.getItem(position).id; + postDelayed(new Runnable() { + public void run() { + switchUser(userId); + } + }, 100); + } + } + + @Override + public void onClick(View v) { + // Delete was clicked + postDelayed(new Runnable() { + public void run() { + if (mGuestUser != null) { + switchUser(0); + mUserManager.removeUser(mGuestUser.id); + mGuestUser = null; + refreshUsers(); + } + } + }, 100); + } + + private void switchUser(int userId) { try { WindowManagerGlobal.getWindowManagerService().lockNow(null); ActivityManagerNative.getDefault().switchUser(userId); @@ -90,6 +136,15 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem } } + private void switchToGuestUser() { + if (mGuestUser == null) { + // No guest user. Create one. + mGuestUser = mUserManager.createGuest(mContext, + mContext.getResources().getString(R.string.guest_nickname)); + } + switchUser(mGuestUser.id); + } + private void finish() { if (mFinishRunnable != null) { mFinishRunnable.run(); @@ -119,9 +174,12 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem public void refreshUsers() { mUserInfo.clear(); + mGuestUser = null; List<UserInfo> users = mUserManager.getUsers(true); for (UserInfo user : users) { - if (!user.isManagedProfile()) { + if (user.isGuest()) { + mGuestUser = user; + } else if (!user.isManagedProfile()) { mUserInfo.add(user); } } @@ -132,17 +190,25 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem @Override public int getCount() { - return mUserInfo.size(); + return mUserInfo.size() + (mGuestUserEnabled ? 1 : 0); } @Override public UserInfo getItem(int position) { - return mUserInfo.get(position); + if (position < mUserInfo.size()) { + return mUserInfo.get(position); + } else { + return mGuestUser; + } } @Override public long getItemId(int position) { - return getItem(position).serialNumber; + if (position < mUserInfo.size()) { + return getItem(position).serialNumber; + } else { + return mGuestUser != null ? mGuestUser.serialNumber : -1; + } } @Override @@ -161,18 +227,46 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem ViewHolder h = new ViewHolder(); h.name = (TextView) v.findViewById(R.id.user_name); h.picture = (ImageView) v.findViewById(R.id.user_picture); + h.delete = (ImageView) v.findViewById(R.id.user_delete); v.setTag(h); return v; } private void bindView(ViewHolder h, UserInfo item) { - h.name.setText(item.name); - h.picture.setImageBitmap(mUserManager.getUserIcon(item.id)); + if (item != null) { + h.name.setText(item.name); + h.picture.setImageBitmap(circularClip(mUserManager.getUserIcon(item.id))); + h.delete.setVisibility(item.isGuest() ? View.VISIBLE : View.GONE); + h.delete.setOnClickListener(UserSwitcherHostView.this); + if (item.isGuest()) { + h.picture.setImageResource(R.drawable.ic_account_circle); + } + } else { + h.name.setText(R.string.guest_new_guest); + h.picture.setImageResource(R.drawable.ic_account_circle); + h.delete.setVisibility(View.GONE); + } + } + + private Bitmap circularClip(Bitmap input) { + if (input == null) { + return null; + } + Bitmap output = Bitmap.createBitmap(input.getWidth(), + input.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + final Paint paint = new Paint(); + paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); + paint.setAntiAlias(true); + canvas.drawCircle(input.getWidth() / 2, input.getHeight() / 2, input.getWidth() / 2, + paint); + return output; } class ViewHolder { TextView name; ImageView picture; + ImageView delete; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index b91e129..8d19f50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -158,7 +158,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private final Runnable mTapTimeoutRunnable = new Runnable() { @Override public void run() { - makeInactive(); + makeInactive(true /* animate */); } }; @@ -183,7 +183,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView break; case MotionEvent.ACTION_MOVE: if (!isWithinTouchSlop(event)) { - makeInactive(); + makeInactive(true /* animate */); return false; } break; @@ -193,14 +193,17 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView makeActive(); postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS); } else { - performClick(); + boolean performed = performClick(); + if (performed) { + removeCallbacks(mTapTimeoutRunnable); + } } } else { - makeInactive(); + makeInactive(true /* animate */); } break; case MotionEvent.ACTION_CANCEL: - makeInactive(); + makeInactive(true /* animate */); break; default: break; @@ -257,10 +260,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView /** * Cancels the hotspot and makes the notification inactive. */ - private void makeInactive() { + public void makeInactive(boolean animate) { if (mActivated) { if (mDimmed) { - startActivateAnimation(true /* reverse */); + if (animate) { + startActivateAnimation(true /* reverse */); + } else { + mBackgroundNormal.setVisibility(View.INVISIBLE); + } } mActivated = false; } @@ -351,6 +358,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundDimmed.setVisibility(View.INVISIBLE); mBackgroundNormal.setVisibility(View.VISIBLE); mBackgroundNormal.setAlpha(1f); + removeCallbacks(mTapTimeoutRunnable); } } @@ -581,7 +589,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } public interface OnActivatedListener { - void onActivated(View view); - void onActivationReset(View view); + void onActivated(ActivatableNotificationView view); + void onActivationReset(ActivatableNotificationView view); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index a7af998..20684a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -48,7 +48,7 @@ import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; @@ -80,17 +80,17 @@ import com.android.systemui.SearchPanelView; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.phone.KeyguardTouchDelegate; +import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.Locale; import static com.android.keyguard.KeyguardHostView.OnDismissAction; public abstract class BaseStatusBar extends SystemUI implements - CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener { + CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener, + RecentsComponent.Callbacks { public static final String TAG = "StatusBar"; public static final boolean DEBUG = false; public static final boolean MULTIUSER_DEBUG = false; @@ -106,6 +106,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected static final int MSG_SHOW_HEADS_UP = 1026; protected static final int MSG_HIDE_HEADS_UP = 1027; protected static final int MSG_ESCALATE_HEADS_UP = 1028; + protected static final int MSG_DECAY_HEADS_UP = 1029; protected static final boolean ENABLE_HEADS_UP = true; // scores above this threshold should be displayed in heads up mode. @@ -129,8 +130,9 @@ public abstract class BaseStatusBar extends SystemUI implements protected NotificationData mNotificationData = new NotificationData(); protected NotificationStackScrollLayout mStackScroller; - protected NotificationData.Entry mInterruptingNotificationEntry; - protected long mInterruptingNotificationTime; + // for heads up notifications + protected HeadsUpNotificationView mHeadsUpNotificationView; + protected int mHeadsUpNotificationDecay; // used to notify status bar for suppressing notification LED protected boolean mPanelSlightlyVisible; @@ -291,7 +293,7 @@ public abstract class BaseStatusBar extends SystemUI implements public void onListenerConnected() { if (DEBUG) Log.d(TAG, "onListenerConnected"); final StatusBarNotification[] notifications = getActiveNotifications(); - final Ranking currentRanking = getCurrentRanking(); + final RankingMap currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { @@ -303,41 +305,41 @@ public abstract class BaseStatusBar extends SystemUI implements } @Override - public void onNotificationPosted(final StatusBarNotification sbn) { + public void onNotificationPosted(final StatusBarNotification sbn, + final RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); - final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { - if (mNotificationData.findByKey(sbn.getKey()) != null) { - updateNotificationInternal(sbn, currentRanking); + if (mNotificationData.findByKey(sbn.getKey()) != null || + isHeadsUp(sbn.getKey())) { + updateNotificationInternal(sbn, rankingMap); } else { - addNotificationInternal(sbn, currentRanking); + addNotificationInternal(sbn, rankingMap); } } }); } @Override - public void onNotificationRemoved(final StatusBarNotification sbn) { + public void onNotificationRemoved(final StatusBarNotification sbn, + final RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); - final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { - removeNotificationInternal(sbn.getKey(), currentRanking); + removeNotificationInternal(sbn.getKey(), rankingMap); } }); } @Override - public void onNotificationRankingUpdate() { + public void onNotificationRankingUpdate(final RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onRankingUpdate"); - final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { - updateRankingInternal(currentRanking); + updateRankingInternal(rankingMap); } }); } @@ -384,6 +386,7 @@ public abstract class BaseStatusBar extends SystemUI implements ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mRecents = getComponent(RecentsComponent.class); + mRecents.setCallback(this); mLocale = mContext.getResources().getConfiguration().locale; mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale); @@ -466,6 +469,10 @@ public abstract class BaseStatusBar extends SystemUI implements // should be overridden } + public boolean isHeadsUp(String key) { + return mHeadsUpNotificationView != null && mHeadsUpNotificationView.isShowing(key); + } + public boolean notificationIsForCurrentProfiles(StatusBarNotification n) { final int thisUserId = mCurrentUserId; final int notificationUserId = n.getUserId(); @@ -505,8 +512,8 @@ public abstract class BaseStatusBar extends SystemUI implements protected View updateNotificationVetoButton(View row, StatusBarNotification n) { View vetoButton = row.findViewById(R.id.veto); - if (n.isClearable() || (mInterruptingNotificationEntry != null - && mInterruptingNotificationEntry.row == row)) { + if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null + && mHeadsUpNotificationView.getEntry().row == row)) { final String _pkg = n.getPackageName(); final String _tag = n.getTag(); final int _id = n.getId(); @@ -545,12 +552,11 @@ public abstract class BaseStatusBar extends SystemUI implements if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) { // Using custom RemoteViews - if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) { - entry.row.setBackgroundResource(R.drawable.notification_row_legacy_bg); - } else if (version < Build.VERSION_CODES.L) { + if (version >= Build.VERSION_CODES.GINGERBREAD && version < Build.VERSION_CODES.L) { entry.row.setBackgroundResourceIds( com.android.internal.R.drawable.notification_bg, com.android.internal.R.drawable.notification_bg_dim); + entry.legacy = true; } } else { // Using platform templates @@ -764,8 +770,19 @@ public abstract class BaseStatusBar extends SystemUI implements } } + @Override + public void onVisibilityChanged(boolean visible) { + // Do nothing + } + public abstract void resetHeadsUpDecayTimer(); + public abstract void scheduleHeadsUpOpen(); + + public abstract void scheduleHeadsUpClose(); + + public abstract void scheduleHeadsUpEscalation(); + /** * Save the current "public" (locked and secure) state of the lockscreen. */ @@ -797,6 +814,18 @@ public abstract class BaseStatusBar extends SystemUI implements return mUsersAllowingPrivateNotifications.get(userHandle); } + public void onNotificationClear(StatusBarNotification notification) { + try { + mBarService.onNotificationClear( + notification.getPackageName(), + notification.getTag(), + notification.getId(), + notification.getUserId()); + } catch (android.os.RemoteException ex) { + // oh well + } + } + protected class H extends Handler { public void handleMessage(Message m) { Intent intent; @@ -874,10 +903,7 @@ public abstract class BaseStatusBar extends SystemUI implements } public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) { - int minHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height); - int maxHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height); + int maxHeight = mRowMaxHeight; StatusBarNotification sbn = entry.notification; RemoteViews contentView = sbn.getNotification().contentView; RemoteViews bigContentView = sbn.getNotification().bigContentView; @@ -1026,6 +1052,7 @@ public abstract class BaseStatusBar extends SystemUI implements com.android.internal.R.id.text); text.setText("Unlock your device to see this notification."); + entry.autoRedacted = true; // TODO: fill out "time" as well } @@ -1039,7 +1066,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } entry.row = row; - entry.row.setHeightRange(mRowMinHeight, mRowMaxHeight); + entry.row.setHeightRange(mRowMinHeight, maxHeight); entry.row.setOnActivatedListener(this); entry.row.setIsBelowSpeedBump(isBelowSpeedBump(entry.notification)); entry.expanded = contentViewLocal; @@ -1101,7 +1128,7 @@ public abstract class BaseStatusBar extends SystemUI implements try { if (mIsHeadsUp) { - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); + mHeadsUpNotificationView.clear(); } mBarService.onNotificationClick(mNotificationKey); } catch (RemoteException ex) { @@ -1157,7 +1184,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } - protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) { + protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) { NotificationData.Entry entry = mNotificationData.remove(key, ranking); if (entry == null) { Log.w(TAG, "removeNotification for unknown key: " + key); @@ -1197,7 +1224,7 @@ public abstract class BaseStatusBar extends SystemUI implements return entry; } - protected void addNotificationViews(Entry entry, Ranking ranking) { + protected void addNotificationViews(Entry entry, RankingMap ranking) { if (entry == null) { return; } @@ -1206,7 +1233,7 @@ public abstract class BaseStatusBar extends SystemUI implements updateNotifications(); } - private void addNotificationViews(StatusBarNotification notification, Ranking ranking) { + private void addNotificationViews(StatusBarNotification notification, RankingMap ranking) { addNotificationViews(createNotificationViews(notification), ranking); } @@ -1292,9 +1319,9 @@ public abstract class BaseStatusBar extends SystemUI implements } public abstract void addNotificationInternal(StatusBarNotification notification, - Ranking ranking); + RankingMap ranking); - protected abstract void updateRankingInternal(Ranking ranking); + protected abstract void updateRankingInternal(RankingMap ranking); @Override public void removeNotification(String key) { @@ -1303,7 +1330,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } - public abstract void removeNotificationInternal(String key, Ranking ranking); + public abstract void removeNotificationInternal(String key, RankingMap ranking); public void updateNotification(StatusBarNotification notification) { if (!USE_NOTIFICATION_LISTENER) { @@ -1311,12 +1338,18 @@ public abstract class BaseStatusBar extends SystemUI implements } } - public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) { + public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) { if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); - final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey()); + final String key = notification.getKey(); + boolean wasHeadsUp = isHeadsUp(key); + NotificationData.Entry oldEntry; + if (wasHeadsUp) { + oldEntry = mHeadsUpNotificationView.getEntry(); + } else { + oldEntry = mNotificationData.findByKey(key); + } if (oldEntry == null) { - Log.w(TAG, "updateNotification for unknown key: " + notification.getKey()); return; } @@ -1382,61 +1415,99 @@ public abstract class BaseStatusBar extends SystemUI implements && oldPublicContentView.getPackage() != null && oldPublicContentView.getPackage().equals(publicContentView.getPackage()) && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId()); - - boolean updateTicker = notification.getNotification().tickerText != null && !TextUtils.equals(notification.getNotification().tickerText, oldEntry.notification.getNotification().tickerText); + + final boolean shouldInterrupt = shouldInterrupt(notification); + final boolean alertAgain = alertAgain(oldEntry); + boolean updateSuccessful = false; if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged) { - if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey()); + if (DEBUG) Log.d(TAG, "reusing notification for key: " + key); oldEntry.notification = notification; try { - updateNotificationViews(oldEntry, notification); - - if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null - && oldNotification == mInterruptingNotificationEntry.notification) { - if (!shouldInterrupt(notification)) { - if (DEBUG) Log.d(TAG, "no longer interrupts!"); - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); - } else { - if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification); - mInterruptingNotificationEntry.notification = notification; - updateHeadsUpViews(mInterruptingNotificationEntry, notification); + if (oldEntry.icon != null) { + // Update the icon + final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(), + notification.getUser(), + notification.getNotification().icon, + notification.getNotification().iconLevel, + notification.getNotification().number, + notification.getNotification().tickerText); + if (!oldEntry.icon.set(ic)) { + handleNotificationError(notification, "Couldn't update icon: " + ic); + return; } } - // Update the icon. - final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(), - notification.getUser(), - notification.getNotification().icon, notification.getNotification().iconLevel, - notification.getNotification().number, - notification.getNotification().tickerText); - if (!oldEntry.icon.set(ic)) { - handleNotificationError(notification, "Couldn't update icon: " + ic); - return; + if (wasHeadsUp) { + if (shouldInterrupt) { + updateHeadsUpViews(oldEntry, notification); + if (alertAgain) { + resetHeadsUpDecayTimer(); + } + } else { + // we updated the notification above, so release to build a new shade entry + mHeadsUpNotificationView.releaseAndClose(); + return; + } + } else { + if (shouldInterrupt && alertAgain) { + removeNotificationViews(key, ranking); + addNotificationInternal(notification, ranking); //this will pop the headsup + } else { + updateNotificationViews(oldEntry, notification); + } } mNotificationData.updateRanking(ranking); updateNotifications(); + updateSuccessful = true; } catch (RuntimeException e) { // It failed to add cleanly. Log, and remove the view from the panel. Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e); - removeNotificationViews(notification.getKey(), ranking); - addNotificationViews(notification, ranking); } - } else { - if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey()); - if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed")); - removeNotificationViews(notification.getKey(), ranking); - addNotificationViews(notification, ranking); // will also replace the heads up - final NotificationData.Entry newEntry = mNotificationData.findByKey( - notification.getKey()); - final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion(); - if (userChangedExpansion) { - boolean userExpanded = oldEntry.row.isUserExpanded(); - newEntry.row.setUserExpanded(userExpanded); - newEntry.row.notifyHeightChanged(); + } + if (!updateSuccessful) { + if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key); + if (wasHeadsUp) { + if (shouldInterrupt) { + if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key); + Entry newEntry = new Entry(notification, null); + ViewGroup holder = mHeadsUpNotificationView.getHolder(); + if (inflateViewsForHeadsUp(newEntry, holder)) { + mHeadsUpNotificationView.showNotification(newEntry); + if (alertAgain) { + resetHeadsUpDecayTimer(); + } + } else { + Log.w(TAG, "Couldn't create new updated headsup for package " + + contentView.getPackage()); + } + } else { + if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key); + oldEntry.notification = notification; + mHeadsUpNotificationView.releaseAndClose(); + return; + } + } else { + if (shouldInterrupt && alertAgain) { + if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key); + removeNotificationViews(key, ranking); + addNotificationInternal(notification, ranking); //this will pop the headsup + } else { + if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key); + removeNotificationViews(key, ranking); + addNotificationViews(notification, ranking); + final NotificationData.Entry newEntry = mNotificationData.findByKey(key); + final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion(); + if (userChangedExpansion) { + boolean userExpanded = oldEntry.row.isUserExpanded(); + newEntry.row.setUserExpanded(userExpanded); + newEntry.row.notifyHeightChanged(); + } + } } } @@ -1511,11 +1582,17 @@ public abstract class BaseStatusBar extends SystemUI implements } protected void notifyHeadsUpScreenOn(boolean screenOn) { - if (!screenOn && mInterruptingNotificationEntry != null) { - mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP); + if (!screenOn) { + scheduleHeadsUpEscalation(); } } + private boolean alertAgain(Entry entry) { + final StatusBarNotification sbn = entry.notification; + return entry == null || !entry.hasInterrupted() + || (sbn.getNotification().flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0; + } + protected boolean shouldInterrupt(StatusBarNotification sbn) { Notification notification = sbn.getNotification(); // some predicates to make the boolean logic legible diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 84005d1..5bad602 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -209,7 +209,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } public int getMaxExpandHeight() { - return mMaxExpandHeight; + return mShowingPublic ? mRowMinHeight : mMaxExpandHeight; } /** @@ -221,30 +221,35 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { @Override public boolean isContentExpandable() { - return mPrivateLayout.isContentExpandable(); + NotificationContentView showingLayout = getShowingLayout(); + return showingLayout.isContentExpandable(); } @Override public void setActualHeight(int height, boolean notifyListeners) { mPrivateLayout.setActualHeight(height); + mPublicLayout.setActualHeight(height); invalidate(); super.setActualHeight(height, notifyListeners); } @Override public int getMaxHeight() { - return mPrivateLayout.getMaxHeight(); + NotificationContentView showingLayout = getShowingLayout(); + return showingLayout.getMaxHeight(); } @Override public int getMinHeight() { - return mPrivateLayout.getMinHeight(); + NotificationContentView showingLayout = getShowingLayout(); + return showingLayout.getMinHeight(); } @Override public void setClipTopAmount(int clipTopAmount) { super.setClipTopAmount(clipTopAmount); mPrivateLayout.setClipTopAmount(clipTopAmount); + mPublicLayout.setClipTopAmount(clipTopAmount); } public boolean isBelowSpeedBump() { @@ -256,6 +261,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } public void notifyContentUpdated() { + mPublicLayout.notifyContentUpdated(); mPrivateLayout.notifyContentUpdated(); } + + public boolean isShowingLayoutLayouted() { + NotificationContentView showingLayout = getShowingLayout(); + return showingLayout.getWidth() != 0; + } + + private NotificationContentView getShowingLayout() { + return mShowingPublic ? mPublicLayout : mPrivateLayout; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java index 4233ab8..bfa74fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java @@ -21,6 +21,7 @@ import android.content.Context; import android.os.Process; import android.provider.Settings; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.ArraySet; @@ -58,17 +59,18 @@ public class InterceptedNotifications { updateSyntheticNotification(); } - public boolean tryIntercept(StatusBarNotification notification, Ranking ranking) { - if (ranking == null) return false; + public boolean tryIntercept(StatusBarNotification notification, RankingMap rankingMap) { + if (rankingMap == null) return false; if (shouldDisplayIntercepted()) return false; if (mReleased.contains(notification.getKey())) return false; - if (!ranking.isInterceptedByDoNotDisturb(notification.getKey())) return false; + Ranking ranking = rankingMap.getRanking(notification.getKey()); + if (!ranking.isInterceptedByDoNotDisturb()) return false; mIntercepted.put(notification.getKey(), notification); updateSyntheticNotification(); return true; } - public void retryIntercepts(Ranking ranking) { + public void retryIntercepts(RankingMap ranking) { if (ranking == null) return; final int N = mIntercepted.size(); @@ -111,7 +113,7 @@ public class InterceptedNotifications { return; } final Notification n = new Notification.Builder(mContext) - .setSmallIcon(R.drawable.ic_qs_zen_on) + .setSmallIcon(R.drawable.ic_notify_zen) .setContentTitle(mContext.getResources().getQuantityString( R.plurals.zen_mode_notification_title, mIntercepted.size(), mIntercepted.size())) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index f9baecb..5cde979 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -106,7 +106,7 @@ public class NotificationContentView extends FrameLayout { private void selectLayout() { if (mActualHeight <= mSmallHeight || mExpandedChild == null) { - if (mContractedChild.getVisibility() != View.VISIBLE) { + if (mContractedChild != null && mContractedChild.getVisibility() != View.VISIBLE) { mContractedChild.setVisibility(View.VISIBLE); } if (mExpandedChild != null && mExpandedChild.getVisibility() != View.INVISIBLE) { @@ -116,7 +116,7 @@ public class NotificationContentView extends FrameLayout { if (mExpandedChild.getVisibility() != View.VISIBLE) { mExpandedChild.setVisibility(View.VISIBLE); } - if (mContractedChild.getVisibility() != View.INVISIBLE) { + if (mContractedChild != null && mContractedChild.getVisibility() != View.INVISIBLE) { mContractedChild.setVisibility(View.INVISIBLE); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index d829ac0..9921c55 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar; import android.app.Notification; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.view.View; @@ -40,6 +41,9 @@ public class NotificationData { public View expandedPublic; // for insecure lockscreens public View expandedBig; private boolean interruption; + public boolean autoRedacted; // whether the redacted notification was generated by us + public boolean legacy; // whether the notification has a legacy, dark background + public Entry() {} public Entry(StatusBarNotification n, StatusBarIconView ic) { this.key = n.getKey(); @@ -64,15 +68,23 @@ public class NotificationData { public void setInterruption() { interruption = true; } + + public boolean hasInterrupted() { + return interruption; + } } private final ArrayList<Entry> mEntries = new ArrayList<Entry>(); - private Ranking mRanking; + private RankingMap mRanking; private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() { @Override public int compare(Entry a, Entry b) { if (mRanking != null) { - return mRanking.getRank(a.key) - mRanking.getRank(b.key); + Ranking aRanking = mRanking.getRanking(a.key); + Ranking bRanking = mRanking.getRanking(b.key); + int aRank = aRanking != null ? aRanking.getRank() : -1; + int bRank = bRanking != null ? bRanking.getRank() : -1; + return aRank - bRank; } final StatusBarNotification na = a.notification; @@ -105,12 +117,12 @@ public class NotificationData { return null; } - public void add(Entry entry, Ranking ranking) { + public void add(Entry entry, RankingMap ranking) { mEntries.add(entry); updateRankingAndSort(ranking); } - public Entry remove(String key, Ranking ranking) { + public Entry remove(String key, RankingMap ranking) { Entry e = findByKey(key); if (e == null) { return null; @@ -120,7 +132,7 @@ public class NotificationData { return e; } - public void updateRanking(Ranking ranking) { + public void updateRanking(RankingMap ranking) { updateRankingAndSort(ranking); } @@ -134,12 +146,13 @@ public class NotificationData { } } } else { - return mRanking.isAmbient(key); + Ranking ranking = mRanking.getRanking(key); + return ranking != null && ranking.isAmbient(); } return false; } - private void updateRankingAndSort(Ranking ranking) { + private void updateRankingAndSort(RankingMap ranking) { if (ranking != null) { mRanking = ranking; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java index 6819d9b..ce5ab5a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java @@ -65,7 +65,7 @@ public class NotificationOverflowIconsView extends IconMerger { } private void applyColor(Notification notification, StatusBarIconView view) { - if (notification.color != Notification.COLOR_DEFAULT) { + if (notification.color == Notification.COLOR_DEFAULT) { if (mNotificationColorUtil.isGrayscale(view.getDrawable())) { view.setColorFilter(mTintColor, PorterDuff.Mode.MULTIPLY); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java deleted file mode 100644 index 1503072..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2014 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.statusbar; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Outline; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.view.View; - -/** - * An single dot of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} - */ -public class SpeedBumpDotView extends View { - - private final Paint mPaint = new Paint(); - - public SpeedBumpDotView(Context context, AttributeSet attrs) { - super(context, attrs); - mPaint.setAntiAlias(true); - } - - @Override - protected void onDraw(Canvas canvas) { - float radius = getWidth() / 2.0f; - canvas.drawCircle(radius, radius, radius, mPaint); - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } - - public void setColor(int color) { - mPaint.setColor(color); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java deleted file mode 100644 index cac6327..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2014 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.statusbar; - -import android.content.Context; -import android.view.View; -import com.android.systemui.R; - -/** - * The Algorithm of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} which can be - * queried for {@link * com.android.systemui.statusbar.SpeedBumpDotsState} - */ -public class SpeedBumpDotsAlgorithm { - - private final float mDotRadius; - - public SpeedBumpDotsAlgorithm(Context context) { - mDotRadius = context.getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height) - / 2.0f; - } - - public void getState(SpeedBumpDotsState resultState) { - - // First reset the current state and ensure that every View has a ViewState - resultState.resetViewStates(); - - SpeedBumpDotsLayout hostView = resultState.getHostView(); - boolean currentlyVisible = hostView.isCurrentlyVisible(); - resultState.setActiveState(currentlyVisible - ? SpeedBumpDotsState.SHOWN - : SpeedBumpDotsState.HIDDEN); - int hostWidth = hostView.getWidth(); - float layoutWidth = hostWidth - 2 * mDotRadius; - int childCount = hostView.getChildCount(); - float paddingBetween = layoutWidth / (childCount - 1); - float centerY = hostView.getHeight() / 2.0f; - for (int i = 0; i < childCount; i++) { - View child = hostView.getChildAt(i); - SpeedBumpDotsState.ViewState viewState = resultState.getViewStateForView(child); - if (currentlyVisible) { - float xTranslation = i * paddingBetween; - viewState.xTranslation = xTranslation; - viewState.yTranslation = calculateYTranslation(hostView, centerY, xTranslation, - layoutWidth); - } else { - viewState.xTranslation = layoutWidth / 2; - viewState.yTranslation = centerY - mDotRadius; - } - viewState.alpha = currentlyVisible ? 1.0f : 0.0f; - viewState.scale = currentlyVisible ? 1.0f : 0.5f; - } - } - - private float calculateYTranslation(SpeedBumpDotsLayout hostView, float centerY, - float xTranslation, float layoutWidth) { - float t = hostView.getAnimationProgress(); - if (t == 0.0f || t == 1.0f) { - return centerY - mDotRadius; - } - float damping = (0.5f -Math.abs(0.5f - t)) * 1.3f; - float partialOffset = xTranslation / layoutWidth; - float indentFactor = (float) (Math.sin((t + partialOffset * 1.5f) * - Math.PI) * damping); - return (1.0f - indentFactor) * centerY - mDotRadius; - } - -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java deleted file mode 100644 index ddf5215..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2014 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.statusbar; - -import android.animation.TimeAnimator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateDecelerateInterpolator; -import com.android.systemui.R; - -/** - * A layout with a certain number of dots which are integrated in the - * {@link com.android.systemui.statusbar.SpeedBumpView} - */ -public class SpeedBumpDotsLayout extends ViewGroup { - - private static final float DOT_CLICK_ANIMATION_LENGTH = 300; - private final int mDotSize; - private final SpeedBumpDotsAlgorithm mAlgorithm = new SpeedBumpDotsAlgorithm(getContext()); - private final SpeedBumpDotsState mCurrentState = new SpeedBumpDotsState(this); - private boolean mIsCurrentlyVisible = true; - private final ValueAnimator mClickAnimator; - private float mAnimationProgress; - private ValueAnimator.AnimatorUpdateListener mClickUpdateListener - = new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mAnimationProgress = animation.getAnimatedFraction(); - updateChildren(); - } - }; - - public SpeedBumpDotsLayout(Context context, AttributeSet attrs) { - super(context, attrs); - mDotSize = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height); - createDots(context, attrs); - mClickAnimator = TimeAnimator.ofFloat(0, DOT_CLICK_ANIMATION_LENGTH); - mClickAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); - mClickAnimator.addUpdateListener(mClickUpdateListener); - } - - private void createDots(Context context, AttributeSet attrs) { - SpeedBumpDotView blueDot = new SpeedBumpDotView(context, attrs); - blueDot.setColor(getResources().getColor(R.color.speed_bump_dot_blue)); - addView(blueDot); - - SpeedBumpDotView redDot = new SpeedBumpDotView(context, attrs); - redDot.setColor(getResources().getColor(R.color.speed_bump_dot_red)); - addView(redDot); - - SpeedBumpDotView yellowDot = new SpeedBumpDotView(context, attrs); - yellowDot.setColor(getResources().getColor(R.color.speed_bump_dot_yellow)); - addView(yellowDot); - - SpeedBumpDotView greenDot = new SpeedBumpDotView(context, attrs); - greenDot.setColor(getResources().getColor(R.color.speed_bump_dot_green)); - addView(greenDot); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int childWidthSpec = MeasureSpec.makeMeasureSpec(mDotSize, - MeasureSpec.getMode(widthMeasureSpec)); - int childHeightSpec = MeasureSpec.makeMeasureSpec(mDotSize, - MeasureSpec.getMode(heightMeasureSpec)); - measureChildren(childWidthSpec, childHeightSpec); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - child.layout(0, 0, mDotSize, mDotSize); - } - if (changed) { - updateChildren(); - } - } - - private void updateChildren() { - mAlgorithm.getState(mCurrentState); - mCurrentState.apply(); - } - - public void performVisibilityAnimation(boolean visible) { - if (mClickAnimator.isRunning()) { - mClickAnimator.cancel(); - } - mIsCurrentlyVisible = visible; - mAlgorithm.getState(mCurrentState); - mCurrentState.animateToState(); - } - - public void setInvisible() { - mIsCurrentlyVisible = false; - mAlgorithm.getState(mCurrentState); - mCurrentState.apply(); - } - - public boolean isCurrentlyVisible() { - return mIsCurrentlyVisible; - } - - public void performDotClickAnimation() { - if (mClickAnimator.isRunning()) { - // don't perform an animation if it's running already - return; - } - mClickAnimator.start(); - } - - - public float getAnimationProgress() { - return mAnimationProgress; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java deleted file mode 100644 index 4febab1..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2014 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.statusbar; - -import android.view.View; -import android.view.ViewPropertyAnimator; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -import java.util.HashMap; -import java.util.Map; - -/** - * A state of a {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} - */ -public class SpeedBumpDotsState { - - public static final int HIDDEN = 1; - public static final int SHOWN = 2; - private static final int VISIBILITY_ANIMATION_DELAY_PER_ELEMENT = 80; - - private final SpeedBumpDotsLayout mHostView; - private final HashMap<View, ViewState> mStateMap = new HashMap<View, ViewState>(); - private final Interpolator mFastOutSlowInInterpolator; - private int mActiveState = 0; - - public SpeedBumpDotsState(SpeedBumpDotsLayout hostLayout) { - mHostView = hostLayout; - mFastOutSlowInInterpolator = AnimationUtils - .loadInterpolator(hostLayout.getContext(), - android.R.interpolator.fast_out_slow_in); - } - - public SpeedBumpDotsLayout getHostView() { - return mHostView; - } - - public void resetViewStates() { - int numChildren = mHostView.getChildCount(); - for (int i = 0; i < numChildren; i++) { - View child = mHostView.getChildAt(i); - ViewState viewState = mStateMap.get(child); - if (viewState == null) { - viewState = new ViewState(); - mStateMap.put(child, viewState); - } - } - } - - public ViewState getViewStateForView(View requestedView) { - return mStateMap.get(requestedView); - } - - public void apply() { - int childCount = mHostView.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mHostView.getChildAt(i); - ViewState viewState = mStateMap.get(child); - - child.setTranslationX(viewState.xTranslation); - child.setTranslationY(viewState.yTranslation); - child.setScaleX(viewState.scale); - child.setScaleY(viewState.scale); - child.setAlpha(viewState.alpha); - } - } - - public void animateToState() { - int childCount = mHostView.getChildCount(); - int middleIndex = (childCount - 1) / 2; - long delayPerElement = VISIBILITY_ANIMATION_DELAY_PER_ELEMENT; - boolean isAppearing = getActiveState() == SHOWN; - boolean isDisappearing = getActiveState() == HIDDEN; - for (int i = 0; i < childCount; i++) { - int delayIndex; - if (i <= middleIndex) { - delayIndex = i * 2; - } else { - int distToMiddle = i - middleIndex; - delayIndex = (childCount - 1) - (distToMiddle - 1) * 2; - } - long startDelay = 0; - if (isAppearing || isDisappearing) { - if (isDisappearing) { - delayIndex = childCount - 1 - delayIndex; - } - startDelay = delayIndex * delayPerElement; - } - View child = mHostView.getChildAt(i); - ViewState viewState = mStateMap.get(child); - child.animate().setInterpolator(mFastOutSlowInInterpolator) - .setStartDelay(startDelay) - .alpha(viewState.alpha) - .translationX(viewState.xTranslation) - .translationY(viewState.yTranslation) - .scaleX(viewState.scale).scaleY(viewState.scale); - } - } - - public int getActiveState() { - return mActiveState; - } - - public void setActiveState(int mActiveState) { - this.mActiveState = mActiveState; - } - - public static class ViewState { - float xTranslation; - float yTranslation; - float alpha; - float scale; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java index 689d0e9..f80f0fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java @@ -16,71 +16,26 @@ package com.android.systemui.statusbar; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.Outline; import android.util.AttributeSet; -import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.widget.TextView; import com.android.systemui.R; /** * The view representing the separation between important and less important notifications */ -public class SpeedBumpView extends ExpandableView implements View.OnClickListener { +public class SpeedBumpView extends ExpandableView { - private final int mCollapsedHeight; - private final int mDotsHeight; - private final int mTextPaddingInset; - private SpeedBumpDotsLayout mDots; - private AlphaOptimizedView mLineLeft; - private AlphaOptimizedView mLineRight; - private boolean mIsExpanded; - private boolean mDividerVisible = true; - private ValueAnimator mCurrentAnimator; + private final int mSpeedBumpHeight; + private AlphaOptimizedView mLine; + private boolean mIsVisible = true; private final Interpolator mFastOutSlowInInterpolator; - private float mCenterX; - private TextView mExplanationText; - private boolean mExplanationTextVisible = false; - private AnimatorListenerAdapter mHideExplanationListener = new AnimatorListenerAdapter() { - private boolean mCancelled; - - @Override - public void onAnimationEnd(Animator animation) { - if (!mCancelled) { - mExplanationText.setVisibility(View.INVISIBLE); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - @Override - public void onAnimationStart(Animator animation) { - mCancelled = false; - } - }; - private Animator.AnimatorListener mAnimationFinishedListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mCurrentAnimator = null; - } - }; public SpeedBumpView(Context context, AttributeSet attrs) { super(context, attrs); - mCollapsedHeight = getResources() - .getDimensionPixelSize(R.dimen.speed_bump_height_collapsed); - mTextPaddingInset = getResources().getDimensionPixelSize( - R.dimen.speed_bump_text_padding_inset); - mDotsHeight = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height); - setOnClickListener(this); + mSpeedBumpHeight = getResources() + .getDimensionPixelSize(R.dimen.speed_bump_height); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_slow_in); } @@ -88,111 +43,41 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene @Override protected void onFinishInflate() { super.onFinishInflate(); - mDots = (SpeedBumpDotsLayout) findViewById(R.id.speed_bump_dots_layout); - mLineLeft = (AlphaOptimizedView) findViewById(R.id.speedbump_line_left); - mLineRight = (AlphaOptimizedView) findViewById(R.id.speedbump_line_right); - mExplanationText = (TextView) findViewById(R.id.speed_bump_text); - resetExplanationText(); - + mLine = (AlphaOptimizedView) findViewById(R.id.speedbump_line); } @Override protected int getInitialHeight() { - return mCollapsedHeight; + return mSpeedBumpHeight; } @Override public int getIntrinsicHeight() { - if (mCurrentAnimator != null) { - // expand animation is running - return getActualHeight(); - } - return mIsExpanded ? getHeight() : mCollapsedHeight; + return mSpeedBumpHeight; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - Outline outline = new Outline(); - mCenterX = getWidth() / 2; - float centerY = getHeight() / 2; - // TODO: hide outline better - // Temporary workaround to hide outline on a transparent view - int outlineLeft = (int) (mCenterX - getResources().getDisplayMetrics().densityDpi * 8); - int outlineTop = (int) (centerY - mDotsHeight / 2); - outline.setOval(outlineLeft, outlineTop, outlineLeft + mDotsHeight, - outlineTop + mDotsHeight); - setOutline(outline); - mLineLeft.setPivotX(mLineLeft.getWidth()); - mLineLeft.setPivotY(mLineLeft.getHeight() / 2); - mLineRight.setPivotX(0); - mLineRight.setPivotY(mLineRight.getHeight() / 2); + mLine.setPivotX(mLine.getWidth() / 2); + mLine.setPivotY(mLine.getHeight() / 2); + setOutline(null); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); - int height = mCollapsedHeight + mExplanationText.getMeasuredHeight() - mTextPaddingInset; + int height = mSpeedBumpHeight; setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height); } @Override - public void onClick(View v) { - if (mCurrentAnimator != null) { - return; - } - int startValue = mIsExpanded ? getMaxHeight() : mCollapsedHeight; - int endValue = mIsExpanded ? mCollapsedHeight : getMaxHeight(); - mCurrentAnimator = ValueAnimator.ofInt(startValue, endValue); - mCurrentAnimator.setInterpolator(mFastOutSlowInInterpolator); - mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - setActualHeight((int) animation.getAnimatedValue()); - } - }); - mCurrentAnimator.addListener(mAnimationFinishedListener); - mCurrentAnimator.start(); - mIsExpanded = !mIsExpanded; - mDots.performDotClickAnimation(); - animateExplanationTextInternal(mIsExpanded); - } - - private void animateExplanationTextInternal(boolean visible) { - if (mExplanationTextVisible != visible) { - float translationY = 0.0f; - float scale = 0.5f; - float alpha = 0.0f; - boolean needsHideListener = true; - if (visible) { - mExplanationText.setVisibility(VISIBLE); - translationY = mDots.getBottom() - mTextPaddingInset; - scale = 1.0f; - alpha = 1.0f; - needsHideListener = false; - } - mExplanationText.animate().setInterpolator(mFastOutSlowInInterpolator) - .alpha(alpha) - .scaleX(scale) - .scaleY(scale) - .translationY(translationY) - .setListener(needsHideListener ? mHideExplanationListener : null); - mExplanationTextVisible = visible; - } - } - - @Override public boolean isTransparent() { return true; } public void performVisibilityAnimation(boolean nowVisible) { animateDivider(nowVisible, null /* onFinishedRunnable */); - - // Animate explanation Text - if (mIsExpanded) { - animateExplanationTextInternal(nowVisible); - } } /** @@ -203,28 +88,16 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene * finished. */ public void animateDivider(boolean nowVisible, Runnable onFinishedRunnable) { - if (nowVisible != mDividerVisible) { + if (nowVisible != mIsVisible) { // Animate dividers float endValue = nowVisible ? 1.0f : 0.0f; - float endTranslationXLeft = nowVisible ? 0.0f : mCenterX - mLineLeft.getRight(); - float endTranslationXRight = nowVisible ? 0.0f : mCenterX - mLineRight.getLeft(); - mLineLeft.animate() + mLine.animate() .alpha(endValue) .scaleX(endValue) .scaleY(endValue) - .translationX(endTranslationXLeft) .setInterpolator(mFastOutSlowInInterpolator) .withEndAction(onFinishedRunnable); - mLineRight.animate() - .alpha(endValue) - .scaleX(endValue) - .scaleY(endValue) - .translationX(endTranslationXRight) - .setInterpolator(mFastOutSlowInInterpolator); - - // Animate dots - mDots.performVisibilityAnimation(nowVisible); - mDividerVisible = nowVisible; + mIsVisible = nowVisible; } else { if (onFinishedRunnable != null) { onFinishedRunnable.run(); @@ -233,34 +106,10 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene } public void setInvisible() { - float endTranslationXLeft = mCenterX - mLineLeft.getRight(); - float endTranslationXRight = mCenterX - mLineRight.getLeft(); - mLineLeft.setAlpha(0.0f); - mLineLeft.setScaleX(0.0f); - mLineLeft.setScaleY(0.0f); - mLineLeft.setTranslationX(endTranslationXLeft); - mLineRight.setAlpha(0.0f); - mLineRight.setScaleX(0.0f); - mLineRight.setScaleY(0.0f); - mLineRight.setTranslationX(endTranslationXRight); - mDots.setInvisible(); - resetExplanationText(); - - mDividerVisible = false; - } - - public void collapse() { - if (mIsExpanded) { - setActualHeight(mCollapsedHeight); - mIsExpanded = false; - } - resetExplanationText(); - } - - public void animateExplanationText(boolean nowVisible) { - if (mIsExpanded) { - animateExplanationTextInternal(nowVisible); - } + mLine.setAlpha(0.0f); + mLine.setScaleX(0.0f); + mLine.setScaleY(0.0f); + mIsVisible = false; } @Override @@ -272,17 +121,4 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene public void performAddAnimation(long delay) { performVisibilityAnimation(true); } - - private void resetExplanationText() { - mExplanationText.setTranslationY(0); - mExplanationText.setVisibility(INVISIBLE); - mExplanationText.setAlpha(0.0f); - mExplanationText.setScaleX(0.5f); - mExplanationText.setScaleY(0.5f); - mExplanationTextVisible = false; - } - - public boolean isExpanded() { - return mIsExpanded; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 6a83a5e..db85b14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -88,13 +88,15 @@ public class KeyguardClockPositionAlgorithm { public void run(Result result) { int y = getClockY() - mKeyguardStatusHeight/2; - float clockAdjustment = getClockYExpansionAdjustment(); + float topAdjustment = getTopExpansionAdjustment(); float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier(); - result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier); + result.stackScrollerPaddingAdjustment = (int) (topAdjustment*topPaddingAdjMultiplier); int clockNotificationsPadding = getClockNotificationsPadding() + result.stackScrollerPaddingAdjustment; int padding = y + clockNotificationsPadding; - y += clockAdjustment; + if (mNotificationCount == 0) { + y += topAdjustment; + } result.clockY = y; result.stackScrollerPadding = mKeyguardStatusHeight + padding; result.clockAlpha = getClockAlpha(result.stackScrollerPadding @@ -117,8 +119,8 @@ public class KeyguardClockPositionAlgorithm { return (int) (getClockYFraction() * mHeight); } - private float getClockYExpansionAdjustment() { - float rubberbandFactor = getClockYExpansionRubberbandFactor(); + private float getTopExpansionAdjustment() { + float rubberbandFactor = getTopExpansionRubberbandFactor(); float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight)); float t = value / mMaxPanelHeight; float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR @@ -130,7 +132,7 @@ public class KeyguardClockPositionAlgorithm { } } - private float getClockYExpansionRubberbandFactor() { + private float getTopExpansionRubberbandFactor() { float t = getNotificationAmountT(); t = Math.min(t, 1.0f); t = (float) Math.pow(t, 0.3f); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java index 086a266..ca49408 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java @@ -246,7 +246,9 @@ public class KeyguardPageSwipeHelper { private void startHintTranslationAnimations(float target, long duration, Interpolator interpolator) { ArrayList<View> targetViews = mCallback.getTranslationViews(); - for (View targetView : targetViews) { + int length = targetViews.size(); + for (int i = 0; i < length; i++) { + View targetView = targetViews.get(i); targetView.animate() .setDuration(duration) .setInterpolator(interpolator) @@ -259,8 +261,16 @@ public class KeyguardPageSwipeHelper { } private void cancelAnimations() { - ArrayList<View> targetViews = mCallback.getTranslationViews(); - for (View target : targetViews) { + ArrayList<View> translatingViews = mCallback.getTranslationViews(); + int length = translatingViews.size(); + for (int i = 0; i < length; i++) { + View target = translatingViews.get(i); + target.animate().cancel(); + } + ArrayList<View> fadingViews = mCallback.getFadingViews(); + length = fadingViews.size(); + for (int i = 0; i < length; i++) { + View target = fadingViews.get(i); target.animate().cancel(); } View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon; @@ -291,6 +301,11 @@ public class KeyguardPageSwipeHelper { // translation Animation startTranslationAnimations(vel, target); + // fade animations + if (snapBack) { + startFadeInAnimations(); + } + // animate left / right icon startIconAnimation(vel, snapBack, target); @@ -346,9 +361,20 @@ public class KeyguardPageSwipeHelper { mSwipeAnimator = animator; } + private void startFadeInAnimations() { + ArrayList<View> fadingViews = mCallback.getFadingViews(); + int length = fadingViews.size(); + for (int i = 0; i < length; i++) { + View targetView = fadingViews.get(i); + targetView.animate().alpha(1.0f); + } + } + private void startTranslationAnimations(float vel, float target) { ArrayList<View> targetViews = mCallback.getTranslationViews(); - for (View targetView : targetViews) { + int length = targetViews.size(); + for (int i = 0; i < length; i++) { + View targetView = targetViews.get(i); ViewPropertyAnimator animator = targetView.animate(); mFlingAnimationUtils.apply(animator, mTranslation, target, vel); animator.translationX(target); @@ -375,8 +401,18 @@ public class KeyguardPageSwipeHelper { translation = leftSwipePossible() ? translation : Math.min(0, translation); if (translation != mTranslation || isReset) { ArrayList<View> translatedViews = mCallback.getTranslationViews(); - for (View view : translatedViews) { - view.setTranslationX(translation); + int length = translatedViews.size(); + for (int i = 0; i < length; i++) { + View target = translatedViews.get(i); + target.setTranslationX(translation); + } + float targetAlpha = 1.0f - Math.abs(translation / mMinTranslationAmount); + targetAlpha = Math.max(0.0f, targetAlpha); + ArrayList<View> fadingViews = mCallback.getFadingViews(); + length = fadingViews.size(); + for (int i = 0; i < length; i++) { + View view = fadingViews.get(i); + view.setAlpha(targetAlpha); } if (translation == 0.0f) { boolean animate = !isReset; @@ -423,6 +459,7 @@ public class KeyguardPageSwipeHelper { return; } if (!animate) { + view.animate().cancel(); view.setAlpha(alpha); view.setScaleX(scale); view.setScaleY(scale); @@ -465,6 +502,13 @@ public class KeyguardPageSwipeHelper { } public void reset() { + if (mSwipeAnimator != null) { + mSwipeAnimator.cancel(); + } + ArrayList<View> targetViews = mCallback.getTranslationViews(); + for (View view : targetViews) { + view.animate().cancel(); + } setTranslation(0.0f, true); mSwipingInProgress = false; } @@ -486,6 +530,8 @@ public class KeyguardPageSwipeHelper { ArrayList<View> getTranslationViews(); + ArrayList<View> getFadingViews(); + View getLeftIcon(); View getCenterIcon(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 34179cb..5c686fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -108,6 +108,7 @@ public class NotificationPanelView extends PanelView implements private KeyguardBottomAreaView mKeyguardBottomArea; private boolean mBlockTouches; private ArrayList<View> mSwipeTranslationViews = new ArrayList<>(); + private ArrayList<View> mSwipeFadingViews = new ArrayList<>(); public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -152,7 +153,7 @@ public class NotificationPanelView extends PanelView implements android.R.interpolator.fast_out_linear_in); mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area); mSwipeTranslationViews.add(mNotificationStackScroller); - mSwipeTranslationViews.add(mKeyguardStatusView); + mSwipeFadingViews.add(mKeyguardStatusView); mPageSwiper = new KeyguardPageSwipeHelper(this, getContext()); } @@ -695,6 +696,11 @@ public class NotificationPanelView extends PanelView implements } @Override + protected boolean hasNotifications() { + return mNotificationStackScroller.getNotGoneChildCount() > 0; + } + + @Override protected int getMaxPanelHeight() { // TODO: Figure out transition for collapsing when QS is open, adjust height here. int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); @@ -958,6 +964,11 @@ public class NotificationPanelView extends PanelView implements } @Override + public ArrayList<View> getFadingViews() { + return mSwipeFadingViews; + } + + @Override public View getLeftIcon() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL ? mKeyguardBottomArea.getCameraImageView() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 772d0e7..08305dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -27,8 +27,11 @@ import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.ViewConfiguration; +import android.view.ViewTreeObserver; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; +import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import com.android.systemui.R; @@ -42,6 +45,8 @@ public abstract class PanelView extends FrameLayout { public static final boolean DEBUG = PanelBar.DEBUG; public static final String TAG = PanelView.class.getSimpleName(); + private static final long KEYGUARD_MOVE_UP_LENGTH = 300; + private final void logf(String fmt, Object... args) { Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); } @@ -61,12 +66,20 @@ public abstract class PanelView extends FrameLayout { protected int mTouchSlop; protected boolean mHintAnimationRunning; private boolean mOverExpandedBeforeFling; + private boolean mKeyguardMovingUp; + private int mKeyguardMoveUpDistance; + private float mKeyguardFingerHeight; private ValueAnimator mHeightAnimator; private ObjectAnimator mPeekAnimator; private VelocityTrackerInterface mVelocityTracker; private FlingAnimationUtils mFlingAnimationUtils; + /** + * Whether an instant expand request is currently pending and we are just waiting for layout. + */ + private boolean mInstantExpanding; + PanelBar mBar; protected int mMaxPanelHeight = -1; @@ -76,6 +89,8 @@ public abstract class PanelView extends FrameLayout { private Interpolator mLinearOutSlowInInterpolator; private Interpolator mBounceInterpolator; + private Interpolator mKeyguardMoveUpInterpolator; + private final Interpolator mLinearInterpolator = new LinearInterpolator(); protected void onExpandingFinished() { mBar.onExpandingFinished(); @@ -103,6 +118,7 @@ public abstract class PanelView extends FrameLayout { mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); mBounceInterpolator = new BounceInterpolator(); + mKeyguardMoveUpInterpolator = new PathInterpolator(0.6f, 0f, 0.4f, 1f); } protected void loadDimens() { @@ -114,6 +130,8 @@ public abstract class PanelView extends FrameLayout { mTouchSlop = configuration.getScaledTouchSlop(); mHintDistance = res.getDimension(R.dimen.hint_move_distance); mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width); + mKeyguardMoveUpDistance = + res.getDimensionPixelSize(R.dimen.keyguard_panel_move_up_distance); } private void trackMovement(MotionEvent event) { @@ -128,6 +146,9 @@ public abstract class PanelView extends FrameLayout { @Override public boolean onTouchEvent(MotionEvent event) { + if (mInstantExpanding) { + return false; + } /* * We capture touch events here and update the expand height here in case according to @@ -208,8 +229,13 @@ public abstract class PanelView extends FrameLayout { mJustPeeked = false; } if (!mJustPeeked && (!waitForTouchSlop || mTracking)) { - setExpandedHeightInternal(newHeight); - mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); + if (mStatusBar.getBarState() == StatusBarState.KEYGUARD && + !hasNotifications()) { + setExpandedHeightKeyguard(newHeight); + } else { + setExpandedHeightInternal(newHeight); + mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); + } } trackMovement(event); @@ -238,10 +264,56 @@ public abstract class PanelView extends FrameLayout { return !waitForTouchSlop || mTracking; } + protected abstract boolean hasNotifications(); + + private void setExpandedHeightKeyguard(float newHeight) { + mKeyguardFingerHeight = newHeight; + if (newHeight < getMaxPanelHeight() && !mKeyguardMovingUp) { + mKeyguardMovingUp = true; + ValueAnimator anim = createHeightAnimator( + getMaxPanelHeight() - mKeyguardMoveUpDistance); + anim.setDuration(KEYGUARD_MOVE_UP_LENGTH); + anim.setInterpolator(mKeyguardMoveUpInterpolator); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mHeightAnimator = null; + } + }); + mHeightAnimator = anim; + anim.start(); + postOnAnimationDelayed(new Runnable() { + @Override + public void run() { + if (mKeyguardFingerHeight < mExpandedHeight && mHeightAnimator != null + && mKeyguardMovingUp) { + mHeightAnimator.cancel(); + float target = getMaxPanelHeight() - 1.75f * mKeyguardMoveUpDistance; + float diff = mExpandedHeight - target; + ValueAnimator anim = createHeightAnimator(target); + float velocity = 2.5f * mKeyguardMoveUpDistance / + (KEYGUARD_MOVE_UP_LENGTH / 1000f); + anim.setInterpolator(mLinearInterpolator); + anim.setDuration(Math.max(0, (long) (diff / velocity * 1000f))); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mHeightAnimator = null; + } + }); + mHeightAnimator = anim; + anim.start(); + } + } + }, KEYGUARD_MOVE_UP_LENGTH / 2); + } + } + protected abstract boolean hasConflictingGestures(); protected void onTrackingStopped(boolean expand) { mTracking = false; + mKeyguardMovingUp = false; mBar.onTrackingStopped(PanelView.this, expand); } @@ -263,6 +335,9 @@ public abstract class PanelView extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (mInstantExpanding) { + return false; + } /* * If the user drags anywhere inside the panel we intercept it if he moves his finger @@ -369,6 +444,9 @@ public abstract class PanelView extends FrameLayout { protected void fling(float vel, boolean expand) { cancelPeek(); + if (mHeightAnimator != null) { + mHeightAnimator.cancel(); + } float target = expand ? getMaxPanelHeight() : 0.0f; if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) { onExpandingFinished(); @@ -556,6 +634,41 @@ public abstract class PanelView extends FrameLayout { } } + public void instantExpand() { + mInstantExpanding = true; + abortAnimations(); + if (mTracking) { + onTrackingStopped(true /* expands */); // The panel is expanded after this call. + onExpandingFinished(); + } + setVisibility(VISIBLE); + + // Wait for window manager to pickup the change, so we know the maximum height of the panel + // then. + getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (mStatusBar.getStatusBarWindow().getHeight() + != mStatusBar.getStatusBarHeight()) { + getViewTreeObserver().removeOnGlobalLayoutListener(this); + setExpandedFraction(1f); + mInstantExpanding = false; + } + } + }); + + // Make sure a layout really happens. + requestLayout(); + } + + private void abortAnimations() { + cancelPeek(); + if (mHeightAnimator != null) { + mHeightAnimator.cancel(); + } + } + protected void startUnlockHintAnimation() { // We don't need to hint the user if an animation is already running or the user is changing 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 f86572d..b23992d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -64,7 +64,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Global; -import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.DisplayMetrics; @@ -103,6 +103,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.qs.CircularClipper; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; @@ -277,10 +278,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // the date view DateView mDateView; - // for heads up notifications - private HeadsUpNotificationView mHeadsUpNotificationView; - private int mHeadsUpNotificationDecay; - // on-screen navigation buttons private NavigationBarView mNavigationBarView = null; private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; @@ -366,7 +363,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (!mUseHeadsUp) { Log.d(TAG, "dismissing any existing heads up notification on disable event"); setHeadsUpVisibility(false); - mHeadsUpNotificationView.setNotification(null); + mHeadsUpNotificationView.release(); removeHeadsUpView(); } else { addHeadsUpView(); @@ -818,6 +815,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mStatusBarView; } + public StatusBarWindowView getStatusBarWindow() { + return mStatusBarWindow; + } + @Override protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) { boolean opaque = false; @@ -1050,7 +1051,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) { + public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) { if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey()); if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification, ranking)) { // Forward the ranking so we can sort the new notification. @@ -1061,31 +1062,26 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, displayNotification(notification, ranking); } - public void displayNotification(StatusBarNotification notification, - Ranking ranking) { - Entry shadeEntry = createNotificationViews(notification); - if (shadeEntry == null) { - return; - } + public void displayNotification(StatusBarNotification notification, RankingMap ranking) { if (mUseHeadsUp && shouldInterrupt(notification)) { if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); Entry interruptionCandidate = new Entry(notification, null); ViewGroup holder = mHeadsUpNotificationView.getHolder(); if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { - mInterruptingNotificationTime = System.currentTimeMillis(); - mInterruptingNotificationEntry = interruptionCandidate; - shadeEntry.setInterruption(); - // 1. Populate mHeadsUpNotificationView - mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry); + mHeadsUpNotificationView.showNotification(interruptionCandidate); - // 2. Animate mHeadsUpNotificationView in - mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); - - // 3. Set alarm to age the notification off - resetHeadsUpDecayTimer(); + // do not show the notification in the shade, yet. + return; } - } else if (notification.getNotification().fullScreenIntent != null) { + } + + Entry shadeEntry = createNotificationViews(notification); + if (shadeEntry == null) { + return; + } + + if (notification.getNotification().fullScreenIntent != null) { // Stop screensaver if the notification has a full-screen intent. // (like an incoming phone call) awakenDreams(); @@ -1100,7 +1096,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // usual case: status bar visible & not immersive // show the ticker if there isn't already a heads up - if (mInterruptingNotificationEntry == null) { + if (mHeadsUpNotificationView.getEntry() == null) { tick(notification, true); } } @@ -1110,31 +1106,64 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, updateExpandedViewPos(EXPANDED_LEAVE_ALONE); } + public void displayNotificationFromHeadsUp(StatusBarNotification notification) { + NotificationData.Entry shadeEntry = createNotificationViews(notification); + if (shadeEntry == null) { + return; + } + shadeEntry.setInterruption(); + + addNotificationViews(shadeEntry, null); + // Recalculate the position of the sliding windows and the titles. + setAreThereNotifications(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + } + @Override public void resetHeadsUpDecayTimer() { - mHandler.removeMessages(MSG_HIDE_HEADS_UP); + mHandler.removeMessages(MSG_DECAY_HEADS_UP); if (mUseHeadsUp && mHeadsUpNotificationDecay > 0 && mHeadsUpNotificationView.isClearable()) { - mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay); + mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay); } } @Override - public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) { + public void scheduleHeadsUpOpen() { + mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); + } + + @Override + public void scheduleHeadsUpClose() { + mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); + } + + @Override + public void scheduleHeadsUpEscalation() { + mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP); + } + + @Override + public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) { super.updateNotificationInternal(notification, ranking); // if we're here, then the notification is already in the shade mIntercepted.remove(notification.getKey()); } @Override - protected void updateRankingInternal(Ranking ranking) { + protected void updateRankingInternal(RankingMap ranking) { mNotificationData.updateRanking(ranking); mIntercepted.retryIntercepts(ranking); updateNotifications(); } @Override - public void removeNotificationInternal(String key, Ranking ranking) { + public void removeNotificationInternal(String key, RankingMap ranking) { + if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null + && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) { + mHeadsUpNotificationView.clear(); + } + StatusBarNotification old = removeNotificationViews(key, ranking); if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); @@ -1147,11 +1176,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Recalculate the position of the sliding windows and the titles. updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null - && old == mInterruptingNotificationEntry.notification) { - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); - } - if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 && !mNotificationPanel.isTracking() && mState != StatusBarState.KEYGUARD) { animateCollapsePanels(); @@ -1192,9 +1216,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final int vis = ent.notification.getNotification().visibility; if (vis != Notification.VISIBILITY_SECRET) { // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications - ent.row.setShowingPublic(isLockscreenPublicMode() + boolean showingPublic = isLockscreenPublicMode() && vis == Notification.VISIBILITY_PRIVATE - && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId())); + && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId()); + ent.row.setShowingPublic(showingPublic); + if (ent.autoRedacted && ent.legacy) { + if (showingPublic) { + ent.row.setBackgroundResourceIds( + com.android.internal.R.drawable.notification_material_bg, + com.android.internal.R.drawable.notification_material_bg_dim); + } else { + ent.row.setBackgroundResourceIds( + com.android.internal.R.drawable.notification_bg, + com.android.internal.R.drawable.notification_bg_dim); + } + } toShow.add(ent.row); } } @@ -1562,7 +1598,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, case MSG_SHOW_HEADS_UP: setHeadsUpVisibility(true); break; + case MSG_DECAY_HEADS_UP: + mHeadsUpNotificationView.release(); + setHeadsUpVisibility(false); + break; case MSG_HIDE_HEADS_UP: + mHeadsUpNotificationView.release(); setHeadsUpVisibility(false); break; case MSG_ESCALATE_HEADS_UP: @@ -1575,8 +1616,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, /** if the interrupting notification had a fullscreen intent, fire it now. */ private void escalateHeadsUp() { - if (mInterruptingNotificationEntry != null) { - final StatusBarNotification sbn = mInterruptingNotificationEntry.notification; + if (mHeadsUpNotificationView.getEntry() != null) { + final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification; + mHeadsUpNotificationView.release(); final Notification notification = sbn.getNotification(); if (notification.fullScreenIntent != null) { if (DEBUG) @@ -1664,7 +1706,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } if (mStatusBarWindow != null) { - // release focus immediately to kick off focus change transition mStatusBarWindowManager.setStatusBarFocusable(false); @@ -1906,6 +1947,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, Integer.toHexString(oldVal), Integer.toHexString(newVal), Integer.toHexString(diff))); if (diff != 0) { + // we never set the recents bit via this method, so save the prior state to prevent + // clobbering the bit below + final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0; + mSystemUiVisibility = newVal; // update low profile @@ -1960,6 +2005,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; } + // restore the recents bit + if (wasRecentsVisible) { + mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; + } + // send updated sysui visibility to window manager notifyUiVisibilityChanged(mSystemUiVisibility); } @@ -2231,7 +2281,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, pw.print(" mUseHeadsUp="); pw.println(mUseHeadsUp); pw.print(" interrupting package: "); - pw.println(hunStateToString(mInterruptingNotificationEntry)); + pw.println(hunStateToString(mHeadsUpNotificationView.getEntry())); dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); if (mNavigationBarView != null) { pw.print(" mNavigationBarWindowState="); @@ -2505,26 +2555,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (!ENABLE_HEADS_UP) return; if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window"); mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE); - if (!vis) { - if (DEBUG) Log.d(TAG, "setting heads up entry to null"); - mInterruptingNotificationEntry = null; - } } public void onHeadsUpDismissed() { - if (mInterruptingNotificationEntry == null) return; - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); - if (mHeadsUpNotificationView.isClearable()) { - try { - mBarService.onNotificationClear( - mInterruptingNotificationEntry.notification.getPackageName(), - mInterruptingNotificationEntry.notification.getTag(), - mInterruptingNotificationEntry.notification.getId(), - mInterruptingNotificationEntry.notification.getUserId()); - } catch (android.os.RemoteException ex) { - // oh well - } - } + mHeadsUpNotificationView.dismiss(); } /** @@ -2893,7 +2927,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNotificationPanel.setKeyguardShowing(false); mScrimController.setKeyguardShowing(false); } - updateStackScrollerState(); updatePublicMode(); updateNotifications(); @@ -2909,6 +2942,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, ? View.INVISIBLE : View.VISIBLE); mStackScroller.setScrollingEnabled(!onKeyguard); mStackScroller.setExpandingEnabled(!onKeyguard); + ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); + mStackScroller.setActivatedChild(null); + if (activatedChild != null) { + activatedChild.makeInactive(false /* animate */); + } } public void userActivity() { @@ -2942,22 +2980,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void instantExpandNotificationsPanel() { - // Make our window larger and the panel visible. + // Make our window larger and the panel expanded. makeExpandedVisible(true); - mNotificationPanel.setVisibility(View.VISIBLE); - - // Wait for window manager to pickup the change, so we know the maximum height of the panel - // then. - mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (mStatusBarWindow.getHeight() != getStatusBarHeight()) { - mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this); - mNotificationPanel.setExpandedFraction(1); - } - } - }); + mNotificationPanel.instantExpand(); } private void instantCollapseNotificationPanel() { @@ -2965,9 +2990,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void onActivated(View view) { + public void onActivated(ActivatableNotificationView view) { userActivity(); mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again); + ActivatableNotificationView previousView = mStackScroller.getActivatedChild(); + if (previousView != null) { + previousView.makeInactive(true /* animate */); + } mStackScroller.setActivatedChild(view); } @@ -2980,7 +3009,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void onActivationReset(View view) { + public void onActivationReset(ActivatableNotificationView view) { if (view == mStackScroller.getActivatedChild()) { mKeyguardIndicationController.hideTransientIndication(); mStackScroller.setActivatedChild(null); @@ -3111,4 +3140,41 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } }; + + // Recents + + @Override + protected void showRecents(boolean triggeredFromAltTab) { + // Set the recents visibility flag + mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; + notifyUiVisibilityChanged(mSystemUiVisibility); + super.showRecents(triggeredFromAltTab); + } + + @Override + protected void hideRecents(boolean triggeredFromAltTab) { + // Unset the recents visibility flag + mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; + notifyUiVisibilityChanged(mSystemUiVisibility); + super.hideRecents(triggeredFromAltTab); + } + + @Override + protected void toggleRecents() { + // Toggle the recents visibility flag + mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE; + notifyUiVisibilityChanged(mSystemUiVisibility); + super.toggleRecents(); + } + + @Override + public void onVisibilityChanged(boolean visible) { + // Update the recents visibility flag + if (visible) { + mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; + } else { + mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; + } + notifyUiVisibilityChanged(mSystemUiVisibility); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index 1916f13..c8ab027 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -146,6 +146,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL updateBrightnessControllerState(); updateZTranslation(); updateClickTargets(); + updateWidth(); if (mQSPanel != null) { mQSPanel.setExpanded(expanded && !overscrolled); } @@ -181,7 +182,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } private void updateWidth() { - int width = mKeyguardShowing ? mKeyguardWidth : mNormalWidth; + int width = (mKeyguardShowing && !mExpanded) ? mKeyguardWidth : mNormalWidth; ViewGroup.LayoutParams lp = getLayoutParams(); if (width != lp.width) { lp.width = width; @@ -335,6 +336,11 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } }; + @Override + public boolean shouldDelayChildPressedState() { + return true; + } + public void setShowEmergencyCallsOnly(boolean show) { mShowEmergencyCallsOnly = show; if (mExpanded) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java index df01c12..0a48e34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java @@ -33,9 +33,9 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.SwipeHelper; -import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.phone.PhoneStatusBar; public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback, ViewTreeObserver.OnComputeInternalInsetsListener { @@ -51,7 +51,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. private SwipeHelper mSwipeHelper; private EdgeSwipeHelper mEdgeSwipeHelper; - private BaseStatusBar mBar; + private PhoneStatusBar mBar; private ExpandHelper mExpandHelper; private long mStartTouchTime; @@ -69,7 +69,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay); } - public void setBar(BaseStatusBar bar) { + public void setBar(PhoneStatusBar bar) { mBar = bar; } @@ -77,7 +77,12 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. return mContentHolder; } - public boolean setNotification(NotificationData.Entry headsUp) { + public boolean showNotification(NotificationData.Entry headsUp) { + if (mHeadsUp != null && headsUp != null && !mHeadsUp.key.equals(headsUp.key)) { + // bump any previous heads up back to the shade + release(); + } + mHeadsUp = headsUp; if (mContentHolder != null) { mContentHolder.removeAllViews(); @@ -97,10 +102,57 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. mSwipeHelper.snapChild(mContentHolder, 1f); mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay; + + mHeadsUp.setInterruption(); + + // 2. Animate mHeadsUpNotificationView in + mBar.scheduleHeadsUpOpen(); + + // 3. Set alarm to age the notification off + mBar.resetHeadsUpDecayTimer(); } return true; } + public boolean isShowing(String key) { + return mHeadsUp != null && mHeadsUp.key.equals(key); + } + + /** Discard the Heads Up notification. */ + public void clear() { + mHeadsUp = null; + mBar.scheduleHeadsUpClose(); + } + + /** Respond to dismissal of the Heads Up window. */ + public void dismiss() { + if (mHeadsUp == null) return; + if (mHeadsUp.notification.isClearable()) { + mBar.onNotificationClear(mHeadsUp.notification); + } else { + release(); + } + mHeadsUp = null; + mBar.scheduleHeadsUpClose(); + } + + /** Push any current Heads Up notification down into the shade. */ + public void release() { + if (mHeadsUp != null) { + mBar.displayNotificationFromHeadsUp(mHeadsUp.notification); + } + mHeadsUp = null; + } + + public void releaseAndClose() { + release(); + mBar.scheduleHeadsUpClose(); + } + + public NotificationData.Entry getEntry() { + return mHeadsUp; + } + public boolean isClearable() { return mHeadsUp == null || mHeadsUp.notification.isClearable(); } @@ -125,7 +177,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. if (mHeadsUp != null) { // whoops, we're on already! - setNotification(mHeadsUp); + showNotification(mHeadsUp); } getViewTreeObserver().addOnComputeInternalInsetsListener(this); @@ -282,6 +334,10 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. mTmpTwoArray[1] + mContentHolder.getHeight()); } + public void escalate() { + mBar.scheduleHeadsUpEscalation(); + } + private class EdgeSwipeHelper implements Gefingerpoken { private static final boolean DEBUG_EDGE_SWIPE = false; private final float mTouchSlop; @@ -335,4 +391,4 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. return mConsuming; } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index dc8f315..1f68860 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -30,7 +30,7 @@ public interface NetworkController { void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId, String mobileSignalContentDescriptionId, int dataTypeIconId, boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description); + String dataTypeContentDescriptionId, String description, boolean noSim); void onAirplaneModeChanged(boolean enabled); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index bf908fe..4e54e41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -88,6 +88,7 @@ public class NetworkControllerImpl extends BroadcastReceiver int mQSDataTypeIconId; int mAirplaneIconId; boolean mDataActive; + boolean mNoSim; int mLastSignalLevel; boolean mShowPhoneRSSIForData = false; boolean mShowAtLeastThreeGees = false; @@ -350,18 +351,18 @@ public class NetworkControllerImpl extends BroadcastReceiver if (isEmergencyOnly()) { cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, null); + mContentDescriptionDataType, null, mNoSim); } else { if (mIsWimaxEnabled && mWimaxConnected) { // Wimax is special cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, mNetworkName); + mContentDescriptionDataType, mNetworkName, mNoSim); } else { // Normal mobile data cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, mNetworkName); + mContentDescriptionDataType, mNetworkName, mNoSim); } } cb.onAirplaneModeChanged(mAirplaneMode); @@ -737,6 +738,7 @@ public class NetworkControllerImpl extends BroadcastReceiver // GSM case, we have to check also the sim state if (mSimState == IccCardConstants.State.READY || mSimState == IccCardConstants.State.UNKNOWN) { + mNoSim = false; if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: @@ -759,6 +761,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } } else { iconId = R.drawable.stat_sys_no_sim; + mNoSim = true; visible = false; // no SIM? no data } } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index b21e12c..6d92b05 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.stack; import android.view.View; +import com.android.systemui.statusbar.ActivatableNotificationView; import java.util.ArrayList; @@ -27,7 +28,7 @@ public class AmbientState { private ArrayList<View> mDraggedViews = new ArrayList<View>(); private int mScrollY; private boolean mDimmed; - private View mActivatedChild; + private ActivatableNotificationView mActivatedChild; private float mOverScrollTopAmount; private float mOverScrollBottomAmount; private int mSpeedBumpIndex = -1; @@ -64,7 +65,7 @@ public class AmbientState { * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap * interaction. This child is then scaled normally and its background is fully opaque. */ - public void setActivatedChild(View activatedChild) { + public void setActivatedChild(ActivatableNotificationView activatedChild) { mActivatedChild = activatedChild; } @@ -72,7 +73,7 @@ public class AmbientState { return mDimmed; } - public View getActivatedChild() { + public ActivatableNotificationView getActivatedChild() { return mActivatedChild; } 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 20caed8..94472a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -33,6 +33,7 @@ import android.widget.OverScroller; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SwipeHelper; +import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.SpeedBumpView; @@ -1722,7 +1723,6 @@ public class NotificationStackScrollLayout extends ViewGroup mStackScrollAlgorithm.setIsExpanded(isExpanded); if (!isExpanded) { mOwnScrollY = 0; - mSpeedBumpView.collapse(); } } @@ -1760,7 +1760,7 @@ public class NotificationStackScrollLayout extends ViewGroup /** * See {@link AmbientState#setActivatedChild}. */ - public void setActivatedChild(View activatedChild) { + public void setActivatedChild(ActivatableNotificationView activatedChild) { mAmbientState.setActivatedChild(activatedChild); if (mAnimationsEnabled) { mActivateNeedsAnimation = true; @@ -1769,7 +1769,7 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); } - public View getActivatedChild() { + public ActivatableNotificationView getActivatedChild() { return mAmbientState.getActivatedChild(); } @@ -1791,7 +1791,6 @@ public class NotificationStackScrollLayout extends ViewGroup int newVisibility = visible ? VISIBLE : GONE; mSpeedBumpView.setVisibility(newVisibility); if (visible) { - mSpeedBumpView.collapse(); // Make invisible to ensure that the appear animation is played. mSpeedBumpView.setInvisible(); if (!mIsExpansionChanging) { @@ -1835,6 +1834,11 @@ public class NotificationStackScrollLayout extends ViewGroup return super.dispatchTouchEvent(ev); } + @Override + public boolean shouldDelayChildPressedState() { + return true; + } + /** * A listener that is notified when some child locations might have changed. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index b41f87b..cbad9dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -186,7 +186,7 @@ public class StackScrollAlgorithm { if (!child.isTransparent()) { // Only update the previous values if we are not transparent, // otherwise we would clip to a transparent view. - previousNotificationStart = newYTranslation + child.getClipTopAmount(); + previousNotificationStart = newYTranslation + state.clipTopAmount; previousNotificationEnd = newNotificationEnd; previousNotificationIsSwiped = child.getTranslationX() != 0; } @@ -662,7 +662,7 @@ public class StackScrollAlgorithm { } else { // We are expanding the shade, expand it to its full height. - if (mFirstChildWhileExpanding.getWidth() == 0) { + if (!isMaxSizeInitialized(mFirstChildWhileExpanding)) { // This child was not layouted yet, wait for a layout pass mFirstChildWhileExpanding @@ -689,6 +689,14 @@ public class StackScrollAlgorithm { } } + private boolean isMaxSizeInitialized(ExpandableView child) { + if (child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + return row.isShowingLayoutLayouted(); + } + return child == null || child.getWidth() != 0; + } + private View findFirstVisibleChild(ViewGroup container) { int childCount = container.getChildCount(); for (int i = 0; i < childCount; i++) { 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 94cb16d..1ad4acc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -160,9 +160,8 @@ public class StackScrollState { } if(child instanceof SpeedBumpView) { - float speedBumpEnd = newYTranslation + newHeight; - performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd, - newYTranslation); + float lineEnd = newYTranslation + newHeight / 2; + performSpeedBumpAnimation(i, (SpeedBumpView) child, lineEnd); } } } @@ -183,20 +182,12 @@ public class StackScrollState { child.setClipBounds(mClipRect); } - private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd, - float speedBumpStart) { + private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd) { View nextChild = getNextChildNotGone(i); if (nextChild != null) { ViewState nextState = getViewStateForView(nextChild); - boolean startIsAboveNext = nextState.yTranslation > speedBumpStart; + boolean startIsAboveNext = nextState.yTranslation > speedBumpEnd; speedBump.animateDivider(startIsAboveNext, null /* onFinishedRunnable */); - - // handle expanded case - if (speedBump.isExpanded()) { - boolean endIsAboveNext = nextState.yTranslation > speedBumpEnd; - speedBump.animateExplanationText(endIsAboveNext); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index faea8de..9260aac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -17,14 +17,14 @@ package com.android.systemui.statusbar.tv; import android.os.IBinder; -import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BaseStatusBar; /* @@ -51,11 +51,11 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) { + public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) { } @Override - protected void updateRankingInternal(Ranking ranking) { + protected void updateRankingInternal(RankingMap ranking) { } @Override @@ -63,7 +63,7 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void removeNotificationInternal(String key, Ranking ranking) { + public void removeNotificationInternal(String key, RankingMap ranking) { } @Override @@ -147,6 +147,18 @@ public class TvStatusBar extends BaseStatusBar { } @Override + public void scheduleHeadsUpOpen() { + } + + @Override + public void scheduleHeadsUpEscalation() { + } + + @Override + public void scheduleHeadsUpClose() { + } + + @Override protected int getMaxKeyguardNotifications() { return 0; } @@ -164,10 +176,10 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void onActivated(View view) { + public void onActivated(ActivatableNotificationView view) { } @Override - public void onActivationReset(View view) { + public void onActivationReset(ActivatableNotificationView view) { } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index a8645bc..5e1aa3b 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -93,6 +93,7 @@ import android.view.ViewParent; import android.view.ViewRootImpl; import android.view.ViewStub; import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -118,6 +119,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private final static String TAG = "PhoneWindow"; private final static boolean SWEEP_OPEN_MENU = false; + + private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300; + /** * Simple callback used by the context menu and its submenus. The options * menu submenus do not use this (their behavior is more complex). @@ -246,6 +250,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private Transition mSharedElementExitTransition; private Boolean mAllowExitTransitionOverlap; private Boolean mAllowEnterTransitionOverlap; + private long mBackgroundFadeDurationMillis = -1; static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( @@ -2627,15 +2632,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - protected boolean fitSystemWindows(Rect insets) { - mFrameOffsets.set(insets); + public WindowInsets onApplyWindowInsets(WindowInsets insets) { + mFrameOffsets.set(insets.getSystemWindowInsets()); updateColorViews(insets); - updateStatusGuard(insets); + insets = updateStatusGuard(insets); updateNavigationGuard(insets); if (getForeground() != null) { drawableChanged(); } - return super.fitSystemWindows(insets); + return insets; } @Override @@ -2643,14 +2648,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return false; } - private void updateColorViews(Rect insets) { + private void updateColorViews(WindowInsets insets) { if (mIsFloating || !ActivityManager.isHighEndGfx()) { // No colors on floating windows or low end devices :( return; } if (insets != null) { - mLastTopInset = insets.top; - mLastBottomInset = insets.bottom; + mLastTopInset = insets.getSystemWindowInsetTop(); + mLastBottomInset = insets.getSystemWindowInsetBottom(); } mStatusColorView = updateColorViewInt(mStatusColorView, SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, @@ -2689,7 +2694,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return view; } - private void updateStatusGuard(Rect insets) { + private WindowInsets updateStatusGuard(WindowInsets insets) { boolean showStatusGuard = false; // Show the status guard when the non-overlay contextual action bar is showing if (mActionModeView != null) { @@ -2701,9 +2706,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { && mActionModeView.isShown(); if (nonOverlayShown) { // set top margin to top insets, show status guard - if (mlp.topMargin != insets.top) { + if (mlp.topMargin != insets.getSystemWindowInsetTop()) { mlpChanged = true; - mlp.topMargin = insets.top; + mlp.topMargin = insets.getSystemWindowInsetTop(); if (mStatusGuard == null) { mStatusGuard = new View(mContext); mStatusGuard.setBackgroundColor(mContext.getResources() @@ -2719,7 +2724,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } } - insets.top = 0; // consume top insets + insets = insets.consumeSystemWindowInsets( + false, true /* top */, false, false); showStatusGuard = true; } else { // reset top margin @@ -2736,9 +2742,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mStatusGuard != null) { mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE); } + return insets; } - private void updateNavigationGuard(Rect insets) { + private void updateNavigationGuard(WindowInsets insets) { // IMEs lay out below the nav bar, but the content view must not (for back compat) if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { // prevent the content view from including the nav bar height @@ -2746,7 +2753,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) { MarginLayoutParams mlp = (MarginLayoutParams) mContentParent.getLayoutParams(); - mlp.bottomMargin = insets.bottom; + mlp.bottomMargin = insets.getSystemWindowInsetBottom(); mContentParent.setLayoutParams(mlp); } } @@ -2756,11 +2763,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mNavigationGuard.setBackgroundColor(mContext.getResources() .getColor(R.color.input_method_navigation_guard)); addView(mNavigationGuard, indexOfChild(mNavigationColorView), new LayoutParams( - LayoutParams.MATCH_PARENT, insets.bottom, + LayoutParams.MATCH_PARENT, insets.getSystemWindowInsetBottom(), Gravity.START | Gravity.BOTTOM)); } else { LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams(); - lp.height = insets.bottom; + lp.height = insets.getSystemWindowInsetBottom(); mNavigationGuard.setLayoutParams(lp); } } @@ -3419,6 +3426,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { com.android.internal.R.styleable. Window_windowAllowExitTransitionOverlap, true); } + if (mBackgroundFadeDurationMillis < 0) { + mBackgroundFadeDurationMillis = getWindowStyle().getInteger( + com.android.internal.R.styleable. + Window_windowTransitionBackgroundFadeDuration, + DEFAULT_BACKGROUND_FADE_DURATION_MS); + } } } } @@ -3825,6 +3838,20 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return (mAllowExitTransitionOverlap == null) ? true : mAllowExitTransitionOverlap; } + @Override + public long getTransitionBackgroundFadeDuration() { + return (mBackgroundFadeDurationMillis < 0) ? DEFAULT_BACKGROUND_FADE_DURATION_MS + : mBackgroundFadeDurationMillis; + } + + @Override + public void setTransitionBackgroundFadeDuration(long fadeDurationMillis) { + if (fadeDurationMillis < 0) { + throw new IllegalArgumentException("negative durations are not allowed"); + } + mBackgroundFadeDurationMillis = fadeDurationMillis; + } + private static final class DrawableFeatureState { DrawableFeatureState(int _featureId) { featureId = _featureId; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 03d29c0..a86f466 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -296,6 +296,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowState mLastInputMethodWindow = null; WindowState mLastInputMethodTargetWindow = null; + boolean mRecentsVisible; int mRecentAppsHeldModifiers; boolean mLanguageSwitchKeyPressed; @@ -434,6 +435,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { * be done once per window. */ private WindowState mWinDismissingKeyguard; + /** The window that is currently showing "over" the keyguard. If there is an app window + * belonging to another app on top of this the keyguard shows. If there is a fullscreen + * app window under this, still dismiss the keyguard but don't show the app underneath. Show + * the wallpaper. */ + private WindowState mWinShowWhenLocked; + boolean mShowingLockscreen; boolean mShowingDream; boolean mDreamingLockscreen; @@ -1668,8 +1675,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public boolean doesForceHide(WindowManager.LayoutParams attrs) { - return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0; + public boolean isForceHiding(WindowManager.LayoutParams attrs) { + return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 || + (isKeyguardHostWindow(attrs) && isKeyguardSecureIncludingHidden()); } @Override @@ -2632,6 +2640,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } }); + } else if (mRecentsVisible) { + // Recents is started on top of Home, so when we launch home while recents is open, let + // it do its own animation and then finish itself + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY); + hideRecentApps(false); } else { // no keyguard stuff to worry about, just launch home! try { @@ -2723,6 +2736,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public int adjustSystemUiVisibilityLw(int visibility) { mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); + mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0; // Reset any bits in mForceClearingStatusBarVisibility that // are now clear. @@ -3112,10 +3126,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs, - WindowState attached) { + public void layoutWindowLw(WindowState win, WindowState attached) { // we've already done the status bar - if ((win == mStatusBar && !doesForceHide(attrs)) || win == mNavigationBar) { + final WindowManager.LayoutParams attrs = win.getAttrs(); + if ((win == mStatusBar && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) == 0) || + win == mNavigationBar) { return; } final boolean isDefaultDisplay = win.isDefaultDisplay(); @@ -3596,12 +3611,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDismissKeyguard = DISMISS_KEYGUARD_NONE; mShowingLockscreen = false; mShowingDream = false; + mWinShowWhenLocked = null; } /** {@inheritDoc} */ @Override - public void applyPostLayoutPolicyLw(WindowState win, - WindowManager.LayoutParams attrs) { + public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs) { if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw=" + win.isVisibleOrBehindKeyguardLw()); final int fl = PolicyControl.getWindowFlags(win, attrs); @@ -3639,9 +3654,17 @@ public class PhoneWindowManager implements WindowManagerPolicy { final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0; final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0; + final boolean secureKeyguard = isKeyguardSecure(); if (appWindow) { - if (showWhenLocked || (dismissKeyguard && !isKeyguardSecure())) { + if (showWhenLocked || (dismissKeyguard && !secureKeyguard)) { + // Remove any previous windows with the same appToken. mAppsToBeHidden.remove(win.getAppToken()); + if (mAppsToBeHidden.isEmpty() && showWhenLocked && + isKeyguardSecureIncludingHidden()) { + mWinShowWhenLocked = win; + mHideLockScreen = true; + mForceStatusBarFromKeyguard = false; + } } else { mAppsToBeHidden.add(win.getAppToken()); } @@ -3663,13 +3686,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDismissKeyguard = mWinDismissingKeyguard == win ? DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START; mWinDismissingKeyguard = win; - mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure(); + mForceStatusBarFromKeyguard = mShowingLockscreen && secureKeyguard; } } if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) { mAllowLockscreenWhenOn = true; } } + + if (mWinShowWhenLocked != null && + mWinShowWhenLocked.getAppToken() != win.getAppToken()) { + win.hideLw(false); + } } } } @@ -3677,6 +3705,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public int finishPostLayoutPolicyLw() { + if (mWinShowWhenLocked != null && + mWinShowWhenLocked != mTopFullscreenOpaqueWindowState) { + // A dialog is dismissing the keyguard. Put the wallpaper behind it and hide the + // fullscreen window. + // TODO: Make sure FLAG_SHOW_WALLPAPER is restored when dialog is dismissed. Or not. + mWinShowWhenLocked.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + mTopFullscreenOpaqueWindowState.hideLw(false); + mTopFullscreenOpaqueWindowState = mWinShowWhenLocked; + } + int changes = 0; boolean topIsFullscreen = false; @@ -4632,6 +4670,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { return mKeyguardDelegate.isSecure(); } + // Returns true if keyguard is currently locked whether or not it is currently hidden. + private boolean isKeyguardSecureIncludingHidden() { + return mKeyguardDelegate.isSecure() && mKeyguardDelegate.isShowing(); + } + /** {@inheritDoc} */ public boolean inKeyguardRestrictedKeyInputMode() { if (mKeyguardDelegate == null) return false; diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java index 0e57232..c9bba69 100644 --- a/rs/java/android/renderscript/FieldPacker.java +++ b/rs/java/android/renderscript/FieldPacker.java @@ -233,6 +233,9 @@ public class FieldPacker { if (obj != null) { if (RenderScript.sPointerSize == 8) { addI64(obj.getID(null)); + addI64(0); + addI64(0); + addI64(0); } else { addI32((int)obj.getID(null)); @@ -240,6 +243,9 @@ public class FieldPacker { } else { if (RenderScript.sPointerSize == 8) { addI64(0); + addI64(0); + addI64(0); + addI64(0); } else { addI32(0); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index eca1bc1..1be1572 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1640,6 +1640,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { pw.println("}]"); pw.println(); } + final int windowCount = mSecurityPolicy.mWindows.size(); + for (int j = 0; j < windowCount; j++) { + if (j > 0) { + pw.append(','); + pw.println(); + } + pw.append("Window["); + AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(j); + pw.append(window.toString()); + pw.append(']'); + } } } @@ -3283,7 +3294,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } if (mTouchInteractionInProgress && activeWindowGone) { - mActiveWindowId = INVALID_WINDOW_ID; + mActiveWindowId = mFocusedWindowId; } // Focused window may change the active one, so set the @@ -3291,7 +3302,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { for (int i = 0; i < windowCount; i++) { AccessibilityWindowInfo window = mWindows.get(i); if (window.getId() == mActiveWindowId) { - window.setActive(true); + window.setActive(true); } } } @@ -3336,10 +3347,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // The active window also determined events from which // windows are delivered. synchronized (mLock) { - mFocusedWindowId = getFocusedWindowId(); - if (mWindowsForAccessibilityCallback == null - && windowId == mFocusedWindowId) { - mActiveWindowId = windowId; + if (mWindowsForAccessibilityCallback == null) { + mFocusedWindowId = getFocusedWindowId(); + if (windowId == mFocusedWindowId) { + mActiveWindowId = windowId; + } } } } break; diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index fe5c2ef..aeb195f 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.database.ContentObserver; import android.os.BatteryStats; import com.android.internal.app.IBatteryStats; import com.android.server.am.BatteryStatsService; @@ -149,8 +150,8 @@ public final class BatteryService extends Binder { com.android.internal.R.integer.config_criticalBatteryWarningLevel); mLowBatteryWarningLevel = mContext.getResources().getInteger( com.android.internal.R.integer.config_lowBatteryWarningLevel); - mLowBatteryCloseWarningLevel = mContext.getResources().getInteger( - com.android.internal.R.integer.config_lowBatteryCloseWarningLevel); + mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryCloseWarningBump); mShutdownBatteryTemperature = mContext.getResources().getInteger( com.android.internal.R.integer.config_shutdownBatteryTemperature); @@ -173,9 +174,37 @@ public final class BatteryService extends Binder { void systemReady() { // check our power situation now that it is safe to display the shutdown dialog. synchronized (mLock) { - shutdownIfNoPowerLocked(); - shutdownIfOverTempLocked(); + ContentObserver obs = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + synchronized (mLock) { + updateBatteryWarningLevelLocked(); + } + } + }; + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), + false, obs, UserHandle.USER_ALL); + updateBatteryWarningLevelLocked(); + } + } + + void updateBatteryWarningLevelLocked() { + final ContentResolver resolver = mContext.getContentResolver(); + int defWarnLevel = mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryWarningLevel); + mLowBatteryWarningLevel = Settings.Global.getInt(resolver, + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel); + if (mLowBatteryWarningLevel == 0) { + mLowBatteryWarningLevel = defWarnLevel; + } + if (mLowBatteryWarningLevel < mCriticalBatteryLevel) { + mLowBatteryWarningLevel = mCriticalBatteryLevel; } + mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryCloseWarningBump); + processValuesLocked(true); } /** @@ -232,7 +261,7 @@ public final class BatteryService extends Binder { } } - public boolean isBatteryLowLocked() { + public boolean shouldSendBatteryLowLocked() { final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE; final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE; @@ -299,14 +328,14 @@ public final class BatteryService extends Binder { if (!mUpdatesStopped) { mBatteryProps = props; // Process the new values. - processValuesLocked(); + processValuesLocked(false); } else { mLastBatteryProps.set(props); } } } - private void processValuesLocked() { + private void processValuesLocked(boolean force) { boolean logOutlier = false; long dischargeDuration = 0; @@ -349,14 +378,14 @@ public final class BatteryService extends Binder { shutdownIfNoPowerLocked(); shutdownIfOverTempLocked(); - if (mBatteryProps.batteryStatus != mLastBatteryStatus || + if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus || mBatteryProps.batteryHealth != mLastBatteryHealth || mBatteryProps.batteryPresent != mLastBatteryPresent || mBatteryProps.batteryLevel != mLastBatteryLevel || mPlugType != mLastPlugType || mBatteryProps.batteryVoltage != mLastBatteryVoltage || mBatteryProps.batteryTemperature != mLastBatteryTemperature || - mInvalidCharger != mLastInvalidCharger) { + mInvalidCharger != mLastInvalidCharger)) { if (mPlugType != mLastPlugType) { if (mLastPlugType == BATTERY_PLUGGED_NONE) { @@ -400,7 +429,24 @@ public final class BatteryService extends Binder { logOutlier = true; } - mBatteryLevelLow = isBatteryLowLocked(); + if (!mBatteryLevelLow) { + // Should we now switch in to low battery mode? + if (mPlugType == BATTERY_PLUGGED_NONE + && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) { + mBatteryLevelLow = true; + } + } else { + // Should we now switch out of low battery mode? + if (mPlugType != BATTERY_PLUGGED_NONE) { + mBatteryLevelLow = false; + } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) { + mBatteryLevelLow = false; + } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) { + // If being forced, the previous state doesn't matter, we will just + // absolutely check to see if we are now above the warning level. + mBatteryLevelLow = false; + } + } sendIntentLocked(); @@ -428,7 +474,7 @@ public final class BatteryService extends Binder { }); } - if (mBatteryLevelLow) { + if (shouldSendBatteryLowLocked()) { mSentLowBatteryBroadcast = true; mHandler.post(new Runnable() { @Override @@ -650,7 +696,7 @@ public final class BatteryService extends Binder { long ident = Binder.clearCallingIdentity(); try { mUpdatesStopped = true; - processValuesLocked(); + processValuesLocked(false); } finally { Binder.restoreCallingIdentity(ident); } @@ -664,7 +710,7 @@ public final class BatteryService extends Binder { if (mUpdatesStopped) { mUpdatesStopped = false; mBatteryProps.set(mLastBatteryProps); - processValuesLocked(); + processValuesLocked(false); } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 9087726..00bda06 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -20,7 +20,6 @@ import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; -import static android.net.ConnectivityManager.NetworkCallbackListener; import static android.net.ConnectivityManager.TYPE_BLUETOOTH; import static android.net.ConnectivityManager.TYPE_DUMMY; import static android.net.ConnectivityManager.TYPE_MOBILE; @@ -630,8 +629,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (DBG) log("ConnectivityService starting up"); NetworkCapabilities netCap = new NetworkCapabilities(); - netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId()); NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(), NetworkRequestInfo.REQUEST); @@ -5410,7 +5409,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, int timeoutSec, IBinder binder, int legacyType) { + Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) == false) { enforceConnectivityInternalPermission(); @@ -5418,7 +5417,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { enforceChangePermission(); } - if (timeoutSec < 0 || timeoutSec > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_SEC) { + if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) { throw new IllegalArgumentException("Bad timeout specified"); } NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities( @@ -5428,9 +5427,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkRequestInfo.REQUEST); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri)); - if (timeoutSec > 0) { + if (timeoutMs > 0) { mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST, - nri), timeoutSec * 1000); + nri), timeoutMs); } return networkRequest; } @@ -5694,7 +5693,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { int a2 = 0; switch (notificationType) { case ConnectivityManager.CALLBACK_LOSING: - a1 = 30; // TODO - read this from NetworkMonitor + a1 = 30 * 1000; // TODO - read this from NetworkMonitor // fall through case ConnectivityManager.CALLBACK_PRECHECK: case ConnectivityManager.CALLBACK_AVAILABLE: diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java index 6179082..bcb6e9e 100644 --- a/services/core/java/com/android/server/MountServiceIdler.java +++ b/services/core/java/com/android/server/MountServiceIdler.java @@ -18,32 +18,32 @@ package com.android.server; import java.util.Calendar; -import android.app.task.Task; -import android.app.task.TaskManager; -import android.app.task.TaskParams; -import android.app.task.TaskService; +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.util.Slog; -public class MountServiceIdler extends TaskService { +public class MountServiceIdler extends JobService { private static final String TAG = "MountServiceIdler"; private static ComponentName sIdleService = new ComponentName(MountServiceIdler.class.getPackage().getName(), MountServiceIdler.class.getName()); - private static int MOUNT_TASK_ID = 808; + private static int MOUNT_JOB_ID = 808; private boolean mStarted; - private TaskParams mTaskParams; + private JobParameters mJobParams; private Runnable mFinishCallback = new Runnable() { @Override public void run() { Slog.i(TAG, "Got mount service completion callback"); synchronized (mFinishCallback) { if (mStarted) { - taskFinished(mTaskParams, false); + jobFinished(mJobParams, false); mStarted = false; } } @@ -53,12 +53,12 @@ public class MountServiceIdler extends TaskService { }; @Override - public boolean onStartTask(TaskParams params) { + public boolean onStartJob(JobParameters params) { // The mount service will run an fstrim operation asynchronously // on a designated separate thread, so we provide it with a callback // that lets us cleanly end our idle timeslice. It's safe to call // finishIdle() from any thread. - mTaskParams = params; + mJobParams = params; MountService ms = MountService.sSelf; if (ms != null) { synchronized (mFinishCallback) { @@ -70,9 +70,9 @@ public class MountServiceIdler extends TaskService { } @Override - public boolean onStopTask(TaskParams params) { + public boolean onStopJob(JobParameters params) { // Once we kick off the fstrim we aren't actually interruptible; just note - // that we don't need to call taskFinished(), and let everything happen in + // that we don't need to call jobFinished(), and let everything happen in // the callback from the mount service. synchronized (mFinishCallback) { mStarted = false; @@ -84,12 +84,12 @@ public class MountServiceIdler extends TaskService { * Schedule the idle job that will ping the mount service */ public static void scheduleIdlePass(Context context) { - TaskManager tm = (TaskManager) context.getSystemService(Context.TASK_SERVICE); + JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); Calendar calendar = tomorrowMidnight(); final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis(); - Task.Builder builder = new Task.Builder(MOUNT_TASK_ID, sIdleService); + JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService); builder.setRequiresDeviceIdle(true); builder.setRequiresCharging(true); builder.setMinimumLatency(timeToMidnight); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 32722bc..11f855e 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -906,6 +906,9 @@ final class ActivityRecord { } startTime = 0; finishLaunchTickingLocked(); + if (task != null) { + task.hasBeenVisible = true; + } } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 39800f2..03ce530 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -32,12 +32,12 @@ import static com.android.server.am.ActivityManagerService.VALIDATE_TOKENS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP; +import static com.android.server.am.ActivityStackSupervisor.DEBUG_CONTAINERS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES; import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; -import android.service.voice.IVoiceInteractionSession; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; @@ -74,6 +74,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; +import android.service.voice.IVoiceInteractionSession; import android.util.EventLog; import android.util.Slog; import android.view.Display; @@ -1405,7 +1406,7 @@ final class ActivityStack { ActivityRecord parent = mActivityContainer.mParentActivity; if ((parent != null && parent.state != ActivityState.RESUMED) || - !mActivityContainer.isAttached()) { + !mActivityContainer.isAttachedLocked()) { // Do not resume this stack if its parent is not resumed. // TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st. return false; @@ -2677,11 +2678,14 @@ final class ActivityStack { || prevState == ActivityState.INITIALIZING) { // If this activity is already stopped, we can just finish // it right now. - boolean activityRemoved = destroyActivityLocked(r, true, - oomAdj, "finish-imm"); + r.makeFinishing(); + boolean activityRemoved = destroyActivityLocked(r, true, oomAdj, "finish-imm"); if (activityRemoved) { mStackSupervisor.resumeTopActivitiesLocked(); } + if (DEBUG_CONTAINERS) Slog.d(TAG, + "destroyActivityLocked: finishCurrentActivityLocked r=" + r + + " destroy returned removed=" + activityRemoved); return activityRemoved ? null : r; } @@ -3044,6 +3048,7 @@ final class ActivityStack { if (r != null) { mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r); } + if (DEBUG_CONTAINERS) Slog.d(TAG, "activityDestroyedLocked: r=" + r); if (isInStackLocked(token) != null) { if (r.state == ActivityState.DESTROYING) { @@ -3803,7 +3808,7 @@ final class ActivityStack { mStacks.remove(this); mStacks.add(0, this); } - mActivityContainer.onTaskListEmpty(); + mActivityContainer.onTaskListEmptyLocked(); } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index dcdc610..545a9f7 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -111,6 +111,7 @@ public final class ActivityStackSupervisor implements DisplayListener { static final boolean DEBUG_STATES = DEBUG || false; static final boolean DEBUG_IDLE = DEBUG || false; static final boolean DEBUG_SCREENSHOTS = DEBUG || false; + static final boolean DEBUG_CONTAINERS = DEBUG || false; public static final int HOME_STACK_ID = 0; @@ -135,6 +136,7 @@ public final class ActivityStackSupervisor implements DisplayListener { static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9; static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10; static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11; + static final int CONTAINER_TASK_LIST_EMPTY_TIMEOUT = FIRST_SUPERVISOR_STACK_MSG + 12; private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay"; @@ -239,7 +241,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // TODO: Add listener for removal of references. /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */ - SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>(); + private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>(); /** Mapping from displayId to display current state */ private final SparseArray<ActivityDisplay> mActivityDisplays = @@ -2255,8 +2257,10 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityContainer createActivityContainer(ActivityRecord parentActivity, IActivityContainerCallback callback) { - ActivityContainer activityContainer = new VirtualActivityContainer(parentActivity, callback); + ActivityContainer activityContainer = + new VirtualActivityContainer(parentActivity, callback); mActivityContainers.put(activityContainer.mStackId, activityContainer); + if (DEBUG_CONTAINERS) Slog.d(TAG, "createActivityContainer: " + activityContainer); parentActivity.mChildContainers.add(activityContainer); return activityContainer; } @@ -2265,6 +2269,8 @@ public final class ActivityStackSupervisor implements DisplayListener { final ArrayList<ActivityContainer> childStacks = parentActivity.mChildContainers; for (int containerNdx = childStacks.size() - 1; containerNdx >= 0; --containerNdx) { ActivityContainer container = childStacks.remove(containerNdx); + if (DEBUG_CONTAINERS) Slog.d(TAG, "removeChildActivityContainers: removing " + + container); container.release(); } } @@ -2272,11 +2278,8 @@ public final class ActivityStackSupervisor implements DisplayListener { void deleteActivityContainer(IActivityContainer container) { ActivityContainer activityContainer = (ActivityContainer)container; if (activityContainer != null) { - activityContainer.mStack.finishAllActivitiesLocked(); - final ActivityRecord parent = activityContainer.mParentActivity; - if (parent != null) { - parent.mChildContainers.remove(activityContainer); - } + if (DEBUG_CONTAINERS) Slog.d(TAG, "deleteActivityContainer: ", + new RuntimeException("here").fillInStackTrace()); final int stackId = activityContainer.mStackId; mActivityContainers.remove(stackId); mWindowManager.removeStack(stackId); @@ -3128,13 +3131,20 @@ public final class ActivityStackSupervisor implements DisplayListener { } } } break; + case CONTAINER_TASK_LIST_EMPTY_TIMEOUT: { + synchronized (mService) { + Slog.w(TAG, "Timeout waiting for all activities in task to finish. " + + msg.obj); + ((ActivityContainer) msg.obj).onTaskListEmptyLocked(); + } + } break; } } } class ActivityContainer extends android.app.IActivityContainer.Stub { final static int FORCE_NEW_TASK_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_MULTIPLE_TASK; + Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION; final int mStackId; IActivityContainerCallback mCallback = null; final ActivityStack mStack; @@ -3184,8 +3194,10 @@ public final class ActivityStackSupervisor implements DisplayListener { @Override public int getDisplayId() { - if (mActivityDisplay != null) { - return mActivityDisplay.mDisplayId; + synchronized (mService) { + if (mActivityDisplay != null) { + return mActivityDisplay.mDisplayId; + } } return -1; } @@ -3194,10 +3206,12 @@ public final class ActivityStackSupervisor implements DisplayListener { public boolean injectEvent(InputEvent event) { final long origId = Binder.clearCallingIdentity(); try { - if (mActivityDisplay != null) { - return mInputManagerInternal.injectInputEvent(event, - mActivityDisplay.mDisplayId, - InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + synchronized (mService) { + if (mActivityDisplay != null) { + return mInputManagerInternal.injectInputEvent(event, + mActivityDisplay.mDisplayId, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } } return false; } finally { @@ -3207,10 +3221,16 @@ public final class ActivityStackSupervisor implements DisplayListener { @Override public void release() { - mContainerState = CONTAINER_STATE_FINISHING; - mStack.finishAllActivitiesLocked(); - detachLocked(); - mWindowManager.removeStack(mStackId); + synchronized (mService) { + if (mContainerState == CONTAINER_STATE_FINISHING) { + return; + } + mContainerState = CONTAINER_STATE_FINISHING; + final Message msg = + mHandler.obtainMessage(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this); + mHandler.sendMessageDelayed(msg, 1000); + mStack.finishAllActivitiesLocked(); + } } private void detachLocked() { @@ -3301,15 +3321,17 @@ public final class ActivityStackSupervisor implements DisplayListener { return ActivityStackSupervisor.this; } - boolean isAttached() { + boolean isAttachedLocked() { return mActivityDisplay != null; } void getBounds(Point outBounds) { - if (mActivityDisplay != null) { - mActivityDisplay.getBounds(outBounds); - } else { - outBounds.set(0, 0); + synchronized (mService) { + if (mActivityDisplay != null) { + mActivityDisplay.getBounds(outBounds); + } else { + outBounds.set(0, 0); + } } } @@ -3332,7 +3354,12 @@ public final class ActivityStackSupervisor implements DisplayListener { return true; } - void onTaskListEmpty() { + void onTaskListEmptyLocked() { + mHandler.removeMessages(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this); + if (!mStack.isHomeStack()) { + detachLocked(); + deleteActivityContainer(this); + } mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget(); } @@ -3351,7 +3378,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mParentActivity = parent; mCallback = callback; mContainerState = CONTAINER_STATE_NO_SURFACE; - mIdString = "VirtualActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}"; + mIdString = "VirtualActivityContainer{" + mStackId + ", parent=" + mParentActivity + "}"; } @Override @@ -3397,22 +3424,22 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - setSurfaceIfReady(); + setSurfaceIfReadyLocked(); if (DEBUG_STACK) Slog.d(TAG, "setSurface: " + this + " to display=" + virtualActivityDisplay); } @Override - boolean isAttached() { - return mSurface != null && super.isAttached(); + boolean isAttachedLocked() { + return mSurface != null && super.isAttachedLocked(); } @Override void setDrawn() { synchronized (mService) { mDrawn = true; - setSurfaceIfReady(); + setSurfaceIfReadyLocked(); } } @@ -3422,8 +3449,8 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } - private void setSurfaceIfReady() { - if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn + + private void setSurfaceIfReadyLocked() { + if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReadyLocked: mDrawn=" + mDrawn + " mContainerState=" + mContainerState + " mSurface=" + mSurface); if (mDrawn && mSurface != null && mContainerState == CONTAINER_STATE_NO_SURFACE) { ((VirtualActivityDisplay) mActivityDisplay).setSurface(mSurface); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 79e2d9d..81a0b36 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -73,6 +73,7 @@ final class TaskRecord extends ThumbnailHolder { boolean rootWasReset; // True if the intent at the root of the task had // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag. boolean askedCompatMode;// Have asked the user about compat mode for this task. + boolean hasBeenVisible; // Set if any activities in the task have been visible to the user. String stringName; // caching of toString() result. int userId; // user for which this task was created @@ -328,8 +329,12 @@ final class TaskRecord extends ThumbnailHolder { } boolean autoRemoveFromRecents() { - return intent != null && - (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0; + // We will automatically remove the task either if it has explicitly asked for + // this, or it is empty and has never contained an activity that got shown to + // the user. + return (intent != null && + (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0) || + (mActivities.isEmpty() && !hasBeenVisible); } /** @@ -800,7 +805,8 @@ final class TaskRecord extends ThumbnailHolder { } pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail); pw.print(" lastDescription="); pw.println(lastDescription); - pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); + pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible); + pw.print(" lastActiveTime="); pw.print(lastActiveTime); pw.print(" (inactive for "); pw.print((getInactiveDuration()/1000)); pw.println("s)"); } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 7f43e43..e80aecd 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -111,12 +111,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - static boolean shouldBlank(int state) { - return state == Display.STATE_OFF; - } - - static boolean shouldUnblank(int state) { - return state == Display.STATE_ON || state == Display.STATE_DOZING; + static int getPowerModeForState(int state) { + switch (state) { + case Display.STATE_OFF: + return SurfaceControl.POWER_MODE_OFF; + case Display.STATE_DOZING: + return SurfaceControl.POWER_MODE_DOZE; + default: + return SurfaceControl.POWER_MODE_NORMAL; + } } private final class LocalDisplayDevice extends DisplayDevice { @@ -204,11 +207,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public void requestDisplayStateLocked(int state) { if (mState != state) { - if (shouldBlank(state) && !shouldBlank(mState)) { - SurfaceControl.blankDisplay(getDisplayTokenLocked()); - } else if (shouldUnblank(state) && !shouldUnblank(mState)) { - SurfaceControl.unblankDisplay(getDisplayTokenLocked()); - } + SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(), + getPowerModeForState(state)); mState = state; updateDeviceInfoLocked(); } diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java new file mode 100644 index 0000000..32bcb69 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 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.hdmi; + +import android.annotation.Nullable; +import android.hardware.hdmi.IHdmiControlCallback; +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; +import android.os.RemoteException; +import android.util.Slog; + +/** + * Handles CEC command <Active Source>. + * + * <p>Used by feature actions that need to handle the command in their flow. + */ +final class ActiveSourceHandler { + private static final String TAG = "ActiveSourceHandler"; + + private final HdmiControlService mService; + private final int mSourceAddress; + private final int mSourcePath; + @Nullable private final IHdmiControlCallback mCallback; + + static ActiveSourceHandler create(HdmiControlService service, int sourceAddress, + int sourcePath, IHdmiControlCallback callback) { + if (service == null) { + Slog.e(TAG, "Wrong arguments"); + return null; + } + return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback); + } + + private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath, + IHdmiControlCallback callback) { + mService = service; + mSourceAddress = sourceAddress; + mSourcePath = sourcePath; + mCallback = callback; + } + + /** + * Handles the incoming active source command. + * + * @param deviceLogicalAddress logical address of the device to be the active source + * @param routingPath routing path of the device to be the active source + */ + void process(int deviceLogicalAddress, int routingPath) { + if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) { + invokeCallback(HdmiCec.RESULT_SUCCESS); + return; + } + HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress); + if (device == null) { + // TODO: Start new device action (Device Discovery) sequence 5. + } + + if (!mService.isInPresetInstallationMode()) { + int prevActiveInput = mService.getActiveInput(); + mService.updateActiveDevice(deviceLogicalAddress, routingPath); + if (prevActiveInput != mService.getActiveInput()) { + // TODO: change port input here. + } + invokeCallback(HdmiCec.RESULT_SUCCESS); + } else { + // TV is in a mode that should keep its current source/input from + // being changed for its operation. Reclaim the active source + // or switch the port back to the one used for the current mode. + if (mService.getActiveSource() == mSourceAddress) { + HdmiCecMessage activeSource = + HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath); + mService.sendCecCommand(activeSource); + mService.updateActiveDevice(deviceLogicalAddress, routingPath); + invokeCallback(HdmiCec.RESULT_SUCCESS); + } else { + int activePath = mService.getActivePath(); + mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress, + routingPath, activePath)); + // TODO: Start port select action here + // PortSelectAction action = new PortSelectAction(mService, mSourceAddress, + // activePath, mCallback); + // mService.addActionAndStart(action); + } + } + } + + private void invokeCallback(int result) { + if (mCallback == null) { + return; + } + try { + mCallback.onComplete(result); + } catch (RemoteException e) { + Slog.e(TAG, "Callback failed:" + e); + } + } +} diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index 6f42c8b..d36fc2c 100644 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java @@ -259,7 +259,7 @@ final class DeviceDiscoveryAction extends FeatureAction { byte params[] = cmd.getParams(); if (params.length == 3) { - current.mPhysicalAddress = ((params[0] & 0xFF) << 8) | (params[1] & 0xFF); + current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params); current.mDeviceType = params[2] & 0xFF; increaseProcessedDeviceCount(); @@ -307,9 +307,7 @@ final class DeviceDiscoveryAction extends FeatureAction { byte[] params = cmd.getParams(); if (params.length == 3) { - int vendorId = ((params[0] & 0xFF) << 16) - | ((params[1] & 0xFF) << 8) - | (params[2] & 0xFF); + int vendorId = HdmiUtils.threeBytesToInt(params); current.mVendorId = vendorId; } else { Slog.w(TAG, "Invalid vendor id: " + cmd.toString()); diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java new file mode 100644 index 0000000..f170de0 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2014 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.hdmi; + +import android.hardware.hdmi.IHdmiControlCallback; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; +import android.os.RemoteException; +import android.util.Slog; + +/** + * Handles an action that selects a logical device as a new active source. + * + * Triggered by {@link HdmiTvClient}, attempts to select the given target device + * for a new active source. It does its best to wake up the target in standby mode + * before issuing the command >Set Stream path<. + */ +final class DeviceSelectAction extends FeatureAction { + private static final String TAG = "DeviceSelect"; + + // Time in milliseconds we wait for the device power status to switch to 'Standby' + private static final int TIMEOUT_TRANSIT_TO_STANDBY_MS = 5 * 1000; + + // Time in milliseconds we wait for the device power status to turn to 'On'. + private static final int TIMEOUT_POWER_ON_MS = 5 * 1000; + + // Time in milliseconds we wait for <Active Source>. + private static final int TIMEOUT_ACTIVE_SOURCE_MS = 20 * 1000; + + // The number of times we try to wake up the target device before we give up + // and just send <Set Stream Path>. + private static final int LOOP_COUNTER_MAX = 20; + + // State in which we wait for <Report Power Status> to come in response to the command + // <Give Device Power Status> we have sent. + private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1; + + // State in which we wait for the device power status to switch to 'Standby'. + // We wait till the status becomes 'Standby' before we send <Set Stream Path> + // to wake up the device again. + private static final int STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY = 2; + + // State in which we wait for the device power status to switch to 'on'. We wait + // maximum 100 seconds (20 * 5) before we give up and just send <Set Stream Path>. + private static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3; + + // State in which we wait for the <Active Source> in response to the command + // <Set Stream Path> we have sent. We wait as much as TIMEOUT_ACTIVE_SOURCE_MS + // before we give up and mark the action as failure. + private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 4; + + private final HdmiCecDeviceInfo mTarget; + private final IHdmiControlCallback mCallback; + private final int mSourcePath; + + private int mPowerStatusCounter = 0; + + /** + * Constructor. + * + * @param service {@link HdmiControlService} instance + * @param sourceAddress logical address of TV initiating this action + * @param sourcePath physical address of TV + * @param target target logical device that will be a new active source + * @param callback callback object + */ + public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath, + HdmiCecDeviceInfo target, IHdmiControlCallback callback) { + super(service, sourceAddress); + mCallback = callback; + mSourcePath = sourcePath; + mTarget = target; + } + + @Override + public boolean start() { + // TODO: Call the logic that display a banner saying the select action got started. + queryDevicePowerStatus(); + return true; + } + + private void queryDevicePowerStatus() { + sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus( + mSourceAddress, mTarget.getLogicalAddress())); + mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; + addTimer(mState, TIMEOUT_MS); + } + + @Override + public boolean processCommand(HdmiCecMessage cmd) { + if (cmd.getSource() != mTarget.getLogicalAddress()) { + return false; + } + int opcode = cmd.getOpcode(); + byte[] params = cmd.getParams(); + + switch (mState) { + case STATE_WAIT_FOR_REPORT_POWER_STATUS: + if (opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS && params.length == 1) { + return handleReportPowerStatus(params[0]); + } + return false; + case STATE_WAIT_FOR_ACTIVE_SOURCE: + if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) { + int activePath = HdmiUtils.twoBytesToInt(params); + ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback) + .process(cmd.getSource(), activePath); + finish(); + return true; + } + return false; + default: + break; + } + return false; + } + + private boolean handleReportPowerStatus(int powerStatus) { + // TODO: Check TV's own status which might have been updated during the action. + // If in 'Standby' or 'Transit to standby', remove the banner + // and stop this action. Otherwise, send <Set Stream Path> + switch (powerStatus) { + case HdmiCec.POWER_STATUS_ON: + sendSetStreamPath(); + return true; + case HdmiCec.POWER_STATUS_TRANSIENT_TO_STANDBY: + if (mPowerStatusCounter < 4) { + mState = STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY; + addTimer(mState, TIMEOUT_TRANSIT_TO_STANDBY_MS); + } else { + sendSetStreamPath(); + } + return true; + case HdmiCec.POWER_STATUS_STANDBY: + if (mPowerStatusCounter == 0) { + turnOnDevice(); + } else { + sendSetStreamPath(); + } + return true; + case HdmiCec.POWER_STATUS_TRANSIENT_TO_ON: + if (mPowerStatusCounter < LOOP_COUNTER_MAX) { + mState = STATE_WAIT_FOR_DEVICE_POWER_ON; + addTimer(mState, TIMEOUT_POWER_ON_MS); + } else { + sendSetStreamPath(); + } + return true; + } + return false; + } + + private void turnOnDevice() { + sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER); + sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER_ON_FUNCTION); + mState = STATE_WAIT_FOR_DEVICE_POWER_ON; + addTimer(mState, TIMEOUT_POWER_ON_MS); + } + + private void sendSetStreamPath() { + sendCommand(HdmiCecMessageBuilder.buildSetStreamPath( + mSourceAddress, mTarget.getPhysicalAddress())); + mState = STATE_WAIT_FOR_ACTIVE_SOURCE; + addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS); + } + + private void sendRemoteKeyCommand(int keyCode) { + sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, + mTarget.getLogicalAddress(), keyCode)); + sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, + mTarget.getLogicalAddress())); + } + + @Override + public void handleTimerEvent(int timeoutState) { + if (mState != timeoutState) { + Slog.w(TAG, "Timer in a wrong state. Ignored."); + return; + } + switch (mState) { + case STATE_WAIT_FOR_REPORT_POWER_STATUS: + sendSetStreamPath(); + break; + case STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY: + case STATE_WAIT_FOR_DEVICE_POWER_ON: + mPowerStatusCounter++; + queryDevicePowerStatus(); + break; + case STATE_WAIT_FOR_ACTIVE_SOURCE: + // TODO: Remove the banner + // Display banner "Communication failed. Please check your cable or connection" + invokeCallback(HdmiCec.RESULT_TIMEOUT); + finish(); + break; + } + } + + private void invokeCallback(int result) { + if (mCallback == null) { + return; + } + try { + mCallback.onComplete(result); + } catch (RemoteException e) { + Slog.e(TAG, "Callback failed:" + e); + } + } +} diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java index 0ba7773..ae272b4 100644 --- a/services/core/java/com/android/server/hdmi/FeatureAction.java +++ b/services/core/java/com/android/server/hdmi/FeatureAction.java @@ -46,8 +46,9 @@ abstract class FeatureAction { // Timer handler message used for timeout event protected static final int MSG_TIMEOUT = 100; - // Default timeout for the incoming command to arrive in response to a request - protected static final int TIMEOUT_MS = 1000; + // Default timeout for the incoming command to arrive in response to a request. + // TODO: Consider reading this value from configuration to allow customization. + protected static final int TIMEOUT_MS = 2000; // Default state used in common by all the feature actions. protected static final int STATE_NONE = 0; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 5c420d7..a0c635d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -19,6 +19,7 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.HdmiPortInfo; import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; @@ -308,6 +309,10 @@ final class HdmiCecController { return mDeviceInfos.get(logicalAddress); } + HdmiPortInfo[] getPortInfos() { + return nativeGetPortInfos(mNativePtr); + } + /** * Return the locally hosted logical device of a given type. * @@ -387,6 +392,41 @@ final class HdmiCecController { } /** + * Pass a option to CEC HAL. + * + * @param flag a key of option. For more details, look at + * {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to + * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL} + * @param value a value of option. Actual value varies flag. For more + * details, look at description of flags + */ + void setOption(int flag, int value) { + assertRunOnServiceThread(); + nativeSetOption(mNativePtr, flag, value); + } + + /** + * Configure ARC circuit in the hardware logic to start or stop the feature. + * + * @param enabled whether to enable/disable ARC + */ + void setAudioReturnChannel(boolean enabled) { + assertRunOnServiceThread(); + nativeSetAudioReturnChannel(mNativePtr, enabled); + } + + /** + * Return the connection status of the specified port + * + * @param port port number to check connection status + * @return true if connected; otherwise, return false + */ + boolean isConnected(int port) { + assertRunOnServiceThread(); + return nativeIsConnected(mNativePtr, port); + } + + /** * Poll all remote devices. It sends <Polling Message> to all remote * devices. * @@ -606,4 +646,8 @@ final class HdmiCecController { private static native int nativeGetPhysicalAddress(long controllerPtr); private static native int nativeGetVersion(long controllerPtr); private static native int nativeGetVendorId(long controllerPtr); + private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr); + private static native void nativeSetOption(long controllerPtr, int flag, int value); + private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag); + private static native boolean nativeIsConnected(long controllerPtr, int port); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java new file mode 100644 index 0000000..ebb6f50 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2014 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.hdmi; + +import android.view.KeyEvent; + +/** + * Helper class to translate android keycode to hdmi cec keycode and vice versa. + */ +public class HdmiCecKeycodeTranslator { + public static final int UNSUPPORTED_KEYCODE = -1; + public static final int NO_PARAM = -1; + + // ========================================================================= + // Hdmi CEC keycodes + public static final int CEC_KEYCODE_SELECT = 0x00; + public static final int CEC_KEYCODE_UP = 0x01; + public static final int CEC_KEYCODE_DOWN = 0x02; + public static final int CEC_KEYCODE_LEFT = 0x03; + public static final int CEC_KEYCODE_RIGHT = 0x04; + public static final int CEC_KEYCODE_RIGHT_UP = 0x05; + public static final int CEC_KEYCODE_RIGHT_DOWN = 0x06; + public static final int CEC_KEYCODE_LEFT_UP = 0x07; + public static final int CEC_KEYCODE_LEFT_DOWN = 0x08; + public static final int CEC_KEYCODE_ROOT_MENU = 0x09; + public static final int CEC_KEYCODE_SETUP_MENU = 0x0A; + public static final int CEC_KEYCODE_CONTENTS_MENU = 0x0B; + public static final int CEC_KEYCODE_FAVORITE_MENU = 0x0C; + public static final int CEC_KEYCODE_EXIT = 0x0D; + // RESERVED = 0x0E - 0x0F + public static final int CEC_KEYCODE_MEDIA_TOP_MENU = 0x10; + public static final int CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU = 0x11; + // RESERVED = 0x12 – 0x1C + public static final int CEC_KEYCODE_NUMBER_ENTRY_MODE = 0x1D; + public static final int CEC_KEYCODE_NUMBER_11 = 0x1E; + public static final int CEC_KEYCODE_NUMBER_12 = 0x1F; + public static final int CEC_KEYCODE_NUMBER_0_OR_NUMBER_10 = 0x20; + public static final int CEC_KEYCODE_NUMBERS_1 = 0x21; + public static final int CEC_KEYCODE_NUMBERS_2 = 0x22; + public static final int CEC_KEYCODE_NUMBERS_3 = 0x23; + public static final int CEC_KEYCODE_NUMBERS_4 = 0x24; + public static final int CEC_KEYCODE_NUMBERS_5 = 0x25; + public static final int CEC_KEYCODE_NUMBERS_6 = 0x26; + public static final int CEC_KEYCODE_NUMBERS_7 = 0x27; + public static final int CEC_KEYCODE_NUMBERS_8 = 0x28; + public static final int CEC_KEYCODE_NUMBERS_9 = 0x29; + public static final int CEC_KEYCODE_DOT = 0x2A; + public static final int CEC_KEYCODE_ENTER = 0x2B; + public static final int CEC_KEYCODE_CLEAR = 0x2C; + // RESERVED = 0x2D - 0x2E + public static final int CEC_KEYCODE_NEXT_FAVORITE = 0x2F; + public static final int CEC_KEYCODE_CHANNEL_UP = 0x30; + public static final int CEC_KEYCODE_CHANNEL_DOWN = 0x31; + public static final int CEC_KEYCODE_PREVIOUS_CHANNEL = 0x32; + public static final int CEC_KEYCODE_SOUND_SELECT = 0x33; + public static final int CEC_KEYCODE_INPUT_SELECT = 0x34; + public static final int CEC_KEYCODE_DISPLAY_INFORMATION = 0x35; + public static final int CEC_KEYCODE_HELP = 0x36; + public static final int CEC_KEYCODE_PAGE_UP = 0x37; + public static final int CEC_KEYCODE_PAGE_DOWN = 0x38; + // RESERVED = 0x39 - 0x3F + public static final int CEC_KEYCODE_POWER = 0x40; + public static final int CEC_KEYCODE_VOLUME_UP = 0x41; + public static final int CEC_KEYCODE_VOLUME_DOWN = 0x42; + public static final int CEC_KEYCODE_MUTE = 0x43; + public static final int CEC_KEYCODE_PLAY = 0x44; + public static final int CEC_KEYCODE_STOP = 0x45; + public static final int CEC_KEYCODE_PAUSE = 0x46; + public static final int CEC_KEYCODE_RECORD = 0x47; + public static final int CEC_KEYCODE_REWIND = 0x48; + public static final int CEC_KEYCODE_FAST_FORWARD = 0x49; + public static final int CEC_KEYCODE_EJECT = 0x4A; + public static final int CEC_KEYCODE_FORWARD = 0x4B; + public static final int CEC_KEYCODE_BACKWARD = 0x4C; + public static final int CEC_KEYCODE_STOP_RECORD = 0x4D; + public static final int CEC_KEYCODE_PAUSE_RECORD = 0x4E; + public static final int CEC_KEYCODE_RESERVED = 0x4F; + public static final int CEC_KEYCODE_ANGLE = 0x50; + public static final int CEC_KEYCODE_SUB_PICTURE = 0x51; + public static final int CEC_KEYCODE_VIDEO_ON_DEMAND = 0x52; + public static final int CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE = 0x53; + public static final int CEC_KEYCODE_TIMER_PROGRAMMING = 0x54; + public static final int CEC_KEYCODE_INITIAL_CONFIGURATION = 0x55; + public static final int CEC_KEYCODE_SELECT_BROADCAST_TYPE = 0x56; + public static final int CEC_KEYCODE_SELECT_SOUND_PRESENTATION = 0x57; + // RESERVED = 0x58-0x5F + public static final int CEC_KEYCODE_PLAY_FUNCTION = 0x60; + public static final int CEC_KEYCODE_PAUSE_PLAY_FUNCTION = 0x61; + public static final int CEC_KEYCODE_RECORD_FUNCTION = 0x62; + public static final int CEC_KEYCODE_PAUSE_RECORD_FUNCTION = 0x63; + public static final int CEC_KEYCODE_STOP_FUNCTION = 0x64; + public static final int CEC_KEYCODE_MUTE_FUNCTION = 0x65; + public static final int CEC_KEYCODE_RESTORE_VOLUME_FUNCTION = 0x66; + public static final int CEC_KEYCODE_TUNE_FUNCTION = 0x67; + public static final int CEC_KEYCODE_SELECT_MEDIA_FUNCTION = 0x68; + public static final int CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION = 0x69; + public static final int CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION = 0x6A; + public static final int CEC_KEYCODE_POWER_TOGGLE_FUNCTION = 0x6B; + public static final int CEC_KEYCODE_POWER_OFF_FUNCTION = 0x6C; + public static final int CEC_KEYCODE_POWER_ON_FUNCTION = 0x6D; + // RESERVED = 0x6E-0x70 + public static final int CEC_KEYCODE_F1_BLUE = 0x71; + public static final int CEC_KEYCODE_F2_RED = 0x72; + public static final int CEC_KEYCODE_F3_GREEN = 0x73; + public static final int CEC_KEYCODE_F4_YELLOW = 0x74; + public static final int CEC_KEYCODE_F5 = 0x75; + public static final int CEC_KEYCODE_DATA = 0x76; + // RESERVED = 0x77-0xFF + + // ========================================================================= + // UI Broadcast Type + public static final int UI_BROADCAST_TOGGLE_ALL = 0x00; + public static final int UI_BROADCAST_TOGGLE_ANALOGUE_DIGITAL = 0x01; + public static final int UI_BROADCAST_ANALOGUE = 0x10; + public static final int UI_BROADCAST_ANALOGUE_TERRESTRIAL = 0x20; + public static final int UI_BROADCAST_ANALOGUE_CABLE = 0x30; + public static final int UI_BROADCAST_ANALOGUE_SATELLITE = 0x40; + public static final int UI_BROADCAST_DIGITAL = 0x50; + public static final int UI_BROADCAST_DIGITAL_TERRESTRIAL = 0x60; + public static final int UI_BROADCAST_DIGITAL_CABLE = 0x70; + public static final int UI_BROADCAST_DIGITAL_SATELLITE = 0x80; + public static final int UI_BROADCAST_DIGITAL_COMMNICATIONS_SATELLITE = 0x90; + public static final int UI_BROADCAST_DIGITAL_COMMNICATIONS_SATELLITE_2 = 0x91; + public static final int UI_BROADCAST_IP = 0xA0; + + // ========================================================================= + // UI Sound Presentation Control + public static final int UI_SOUND_PRESENTATION_SOUND_MIX_DUAL_MONO = 0x20; + public static final int UI_SOUND_PRESENTATION_SOUND_MIX_KARAOKE = 0x30; + public static final int UI_SOUND_PRESENTATION_SELECT_AUDIO_DOWN_MIX = 0x80; + public static final int UI_SOUND_PRESENTATION_SELECT_AUDIO_AUTO_REVERBERATION = 0x90; + public static final int UI_SOUND_PRESENTATION_SELECT_AUDIO_AUTO_EQUALIZER = 0xA0; + public static final int UI_SOUND_PRESENTATION_BASS_STEP_PLUS = 0xB1; + public static final int UI_SOUND_PRESENTATION_BASS_NEUTRAL = 0xB2; + public static final int UI_SOUND_PRESENTATION_BASS_STEP_MINUS = 0xB3; + public static final int UI_SOUND_PRESENTATION_TREBLE_STEP_PLUS = 0xC1; + public static final int UI_SOUND_PRESENTATION_TREBLE_NEUTRAL = 0xC2; + public static final int UI_SOUND_PRESENTATION_TREBLE_STEP_MINUS = 0xC3; + + private HdmiCecKeycodeTranslator() { + } + + /** + * A mapping between andorid and cec keycode. + * + * <p>Normal implementation of this looks like + * <pre> + * new KeycodeEntry(KeyEvent.KEYCODE_DPAD_CENTER, CEC_KEYCODE_SELECT); + * </pre> + * <p>However, some keys in CEC requires additional parameter. + * In order to use parameterized cec key, add unique android keycode (existing or custom) + * corresponding to a pair of cec keycode and and its param. + * <pre> + * new KeycodeEntry(CUSTOME_ANDORID_KEY_1, CEC_KEYCODE_SELECT_BROADCAST_TYPE, + * UI_BROADCAST_TOGGLE_ALL); + * new KeycodeEntry(CUSTOME_ANDORID_KEY_2, CEC_KEYCODE_SELECT_BROADCAST_TYPE, + * UI_BROADCAST_ANALOGUE); + * </pre> + */ + private static class KeycodeEntry { + private final int mAndroidKeycode; + private final int mCecKeycode; + private final int mParam; + + private KeycodeEntry(int androidKeycode, int cecKeycode, int param) { + this.mAndroidKeycode = androidKeycode; + this.mCecKeycode = cecKeycode; + this.mParam = param; + } + + private KeycodeEntry(int androidKeycode, int cecKeycode) { + this(androidKeycode, cecKeycode, NO_PARAM); + } + + private byte[] toCecKeycodeIfMatched(int androidKeycode) { + if (mAndroidKeycode == androidKeycode) { + if (mParam == NO_PARAM) { + return new byte[] { + (byte) (mCecKeycode & 0xFF) + }; + } else { + return new byte[] { + (byte) (mCecKeycode & 0xFF), + (byte) (mParam & 0xFF) + }; + } + } else { + return null; + } + } + + private int toAndroidKeycodeIfMatched(int cecKeycode, int param) { + if (cecKeycode == mCecKeycode && mParam == param) { + return mAndroidKeycode; + } else { + return UNSUPPORTED_KEYCODE; + } + } + } + + // Keycode entry container for all mappings. + // Note that order of entry is the same as above cec keycode definition. + private static final KeycodeEntry[] KEYCODE_ENTRIES = new KeycodeEntry[] { + new KeycodeEntry(KeyEvent.KEYCODE_DPAD_CENTER, CEC_KEYCODE_SELECT), + new KeycodeEntry(KeyEvent.KEYCODE_DPAD_UP, CEC_KEYCODE_UP), + new KeycodeEntry(KeyEvent.KEYCODE_DPAD_DOWN, CEC_KEYCODE_DOWN), + new KeycodeEntry(KeyEvent.KEYCODE_DPAD_LEFT, CEC_KEYCODE_LEFT), + new KeycodeEntry(KeyEvent.KEYCODE_DPAD_RIGHT, CEC_KEYCODE_RIGHT), + // No Android keycode defined for CEC_KEYCODE_RIGHT_UP + // No Android keycode defined for CEC_KEYCODE_RIGHT_DOWN + // No Android keycode defined for CEC_KEYCODE_LEFT_UP + // No Android keycode defined for CEC_KEYCODE_LEFT_DOWN + new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU), + new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU), + new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_CONTENTS_MENU), + // No Android keycode defined for CEC_KEYCODE_FAVORITE_MENU + new KeycodeEntry(KeyEvent.KEYCODE_BACK, CEC_KEYCODE_EXIT), + // RESERVED + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_TOP_MENU, CEC_KEYCODE_MEDIA_TOP_MENU), + // No Android keycode defined for CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU + // RESERVED + // No Android keycode defined for CEC_KEYCODE_NUMBER_ENTRY_MODE + new KeycodeEntry(KeyEvent.KEYCODE_11, CEC_KEYCODE_NUMBER_11), + new KeycodeEntry(KeyEvent.KEYCODE_12, CEC_KEYCODE_NUMBER_12), + new KeycodeEntry(KeyEvent.KEYCODE_0, CEC_KEYCODE_NUMBER_0_OR_NUMBER_10), + new KeycodeEntry(KeyEvent.KEYCODE_1, CEC_KEYCODE_NUMBERS_1), + new KeycodeEntry(KeyEvent.KEYCODE_2, CEC_KEYCODE_NUMBERS_2), + new KeycodeEntry(KeyEvent.KEYCODE_3, CEC_KEYCODE_NUMBERS_3), + new KeycodeEntry(KeyEvent.KEYCODE_4, CEC_KEYCODE_NUMBERS_4), + new KeycodeEntry(KeyEvent.KEYCODE_5, CEC_KEYCODE_NUMBERS_5), + new KeycodeEntry(KeyEvent.KEYCODE_6, CEC_KEYCODE_NUMBERS_6), + new KeycodeEntry(KeyEvent.KEYCODE_7, CEC_KEYCODE_NUMBERS_7), + new KeycodeEntry(KeyEvent.KEYCODE_8, CEC_KEYCODE_NUMBERS_8), + new KeycodeEntry(KeyEvent.KEYCODE_9, CEC_KEYCODE_NUMBERS_9), + new KeycodeEntry(KeyEvent.KEYCODE_PERIOD, CEC_KEYCODE_DOT), + new KeycodeEntry(KeyEvent.KEYCODE_NUMPAD_ENTER, CEC_KEYCODE_ENTER), + new KeycodeEntry(KeyEvent.KEYCODE_CLEAR, CEC_KEYCODE_CLEAR), + // RESERVED + // No Android keycode defined for CEC_KEYCODE_NEXT_FAVORITE + new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP), + new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN), + new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL), + // No Android keycode defined for CEC_KEYCODE_SOUND_SELECT + new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT), + new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION), + // No Android keycode defined for CEC_KEYCODE_HELP + new KeycodeEntry(KeyEvent.KEYCODE_PAGE_UP, CEC_KEYCODE_PAGE_UP), + new KeycodeEntry(KeyEvent.KEYCODE_PAGE_DOWN, CEC_KEYCODE_PAGE_DOWN), + // RESERVED + new KeycodeEntry(KeyEvent.KEYCODE_POWER, CEC_KEYCODE_POWER), + new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_UP, CEC_KEYCODE_VOLUME_UP), + new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_DOWN, CEC_KEYCODE_VOLUME_DOWN), + new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_MUTE, CEC_KEYCODE_MUTE), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PLAY, CEC_KEYCODE_PLAY), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_STOP, CEC_KEYCODE_STOP), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PAUSE, CEC_KEYCODE_PAUSE), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_RECORD, CEC_KEYCODE_RECORD), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_REWIND, CEC_KEYCODE_REWIND), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, CEC_KEYCODE_FAST_FORWARD), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_EJECT, CEC_KEYCODE_EJECT), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_NEXT, CEC_KEYCODE_FORWARD), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PREVIOUS, CEC_KEYCODE_BACKWARD), + // No Android keycode defined for CEC_KEYCODE_STOP_RECORD + // No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD + // No Android keycode defined for CEC_KEYCODE_RESERVED + // No Android keycode defined for CEC_KEYCODE_ANGLE + // No Android keycode defined for CEC_KEYCODE_SUB_PICTURE + // No Android keycode defined for CEC_KEYCODE_VIDEO_ON_DEMAND + new KeycodeEntry(KeyEvent.KEYCODE_GUIDE, CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE), + // No Android keycode defined for CEC_KEYCODE_TIMER_PROGRAMMING + // No Android keycode defined for CEC_KEYCODE_INITIAL_CONFIGURATION + // No Android keycode defined for CEC_KEYCODE_SELECT_BROADCAST_TYPE + // No Android keycode defined for CEC_KEYCODE_SELECT_SOUND_PRESENTATION + // RESERVED + // The following deterministic key definitions do not need key mapping + // since they are supposed to be generated programmatically only. + // No Android keycode defined for CEC_KEYCODE_PLAY_FUNCTION + // No Android keycode defined for CEC_KEYCODE_PAUSE_PLAY_FUNCTION + // No Android keycode defined for CEC_KEYCODE_RECORD_FUNCTION + // No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD_FUNCTION + // No Android keycode defined for CEC_KEYCODE_STOP_FUNCTION + // No Android keycode defined for CEC_KEYCODE_MUTE_FUNCTION + // No Android keycode defined for CEC_KEYCODE_RESTORE_VOLUME_FUNCTION + // No Android keycode defined for CEC_KEYCODE_TUNE_FUNCTION + // No Android keycode defined for CEC_KEYCODE_SELECT_MEDIA_FUNCTION + // No Android keycode defined for CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION + // No Android keycode defined for CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION + // No Android keycode defined for CEC_KEYCODE_POWER_TOGGLE_FUNCTION + // No Android keycode defined for CEC_KEYCODE_POWER_OFF_FUNCTION + // No Android keycode defined for CEC_KEYCODE_POWER_ON_FUNCTION + // RESERVED + new KeycodeEntry(KeyEvent.KEYCODE_PROG_BLUE, CEC_KEYCODE_F1_BLUE), + new KeycodeEntry(KeyEvent.KEYCODE_PROG_RED, CEC_KEYCODE_F2_RED), + new KeycodeEntry(KeyEvent.KEYCODE_PROG_GREEN, CEC_KEYCODE_F3_GREEN), + new KeycodeEntry(KeyEvent.KEYCODE_PROG_YELLOW, CEC_KEYCODE_F4_YELLOW), + new KeycodeEntry(KeyEvent.KEYCODE_F5, CEC_KEYCODE_F5), + new KeycodeEntry(KeyEvent.KEYCODE_TV_DATA_SERVICE, CEC_KEYCODE_DATA), + // RESERVED + // Add a new key mapping here if new keycode is introduced. + }; + + /** + * Translate Android keycode to Hdmi Cec keycode. + * + * @param keycode Android keycode. For details, refer {@link KeyEvent} + * @return array of byte which contains cec keycode and param if it has; + * return null if failed to find matched cec keycode + */ + static byte[] androidKeyToCecKey(int keycode) { + for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) { + byte[] cecKeycode = KEYCODE_ENTRIES[i].toCecKeycodeIfMatched(keycode); + if (cecKeycode != null) { + return cecKeycode; + } + } + return null; + } + + /** + * Translate Hdmi CEC keycode to Android keycode. + * + * @param keycode Cec keycode. If has no param, put {@link #NO_PARAM} + * @return cec keycode corresponding to the given android keycode. + * If finds no matched keycode, return {@link #UNSUPPORTED_KEYCODE} + */ + static int cecKeyToAndroidKey(int keycode, int param) { + for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) { + int androidKey = KEYCODE_ENTRIES[i].toAndroidKeycodeIfMatched(keycode, param); + if (androidKey != UNSUPPORTED_KEYCODE) { + return androidKey; + } + } + return UNSUPPORTED_KEYCODE; + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 23454ad..6697a53 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -18,12 +18,15 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; +import android.util.Slog; /** * Class that models a logical CEC device hosted in this system. Handles initialization, * CEC commands that call for actions customized per device type. */ abstract class HdmiCecLocalDevice { + private static final String TAG = "HdmiCecLocalDevice"; protected final HdmiControlService mService; protected final int mDeviceType; @@ -59,6 +62,83 @@ abstract class HdmiCecLocalDevice { */ protected abstract void onAddressAllocated(int logicalAddress); + /** + * Dispatch incoming message. + * + * @param message incoming message + * @return true if consumed a message; otherwise, return false. + */ + final boolean dispatchMessage(HdmiCecMessage message) { + int dest = message.getDestination(); + if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) { + return false; + } + return onMessage(message); + } + + protected boolean onMessage(HdmiCecMessage message) { + switch (message.getOpcode()) { + case HdmiCec.MESSAGE_GET_MENU_LANGUAGE: + return handleGetMenuLanguage(message); + case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS: + return handleGivePhysicalAddress(); + case HdmiCec.MESSAGE_GIVE_OSD_NAME: + return handleGiveOsdName(message); + case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID: + return handleGiveDeviceVendorId(); + case HdmiCec.MESSAGE_GET_CEC_VERSION: + return handleGetCecVersion(message); + default: + return false; + } + } + + protected boolean handleGivePhysicalAddress() { + int physicalAddress = mService.getPhysicalAddress(); + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + mAddress, physicalAddress, mDeviceType); + mService.sendCecCommand(cecMessage); + return true; + } + + protected boolean handleGiveDeviceVendorId() { + int vendorId = mService.getVendorId(); + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand( + mAddress, vendorId); + mService.sendCecCommand(cecMessage); + return true; + } + + protected boolean handleGetCecVersion(HdmiCecMessage message) { + int version = mService.getCecVersion(); + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(), + message.getSource(), version); + mService.sendCecCommand(cecMessage); + return true; + } + + protected boolean handleGetMenuLanguage(HdmiCecMessage message) { + Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString()); + mService.sendCecCommand( + HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress, + message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE, + HdmiConstants.ABORT_UNRECOGNIZED_MODE)); + return true; + } + + protected boolean handleGiveOsdName(HdmiCecMessage message) { + // Note that since this method is called after logical address allocation is done, + // mDeviceInfo should not be null. + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand( + mAddress, message.getSource(), mDeviceInfo.getDisplayName()); + if (cecMessage != null) { + mService.sendCecCommand(cecMessage); + } else { + Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName()); + } + return true; + } + final void handleAddressAllocated(int logicalAddress) { mAddress = mPreferredAddress = logicalAddress; onAddressAllocated(logicalAddress); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 72d7f2d..6394fe7 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -16,12 +16,20 @@ package com.android.server.hdmi; +import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; +import android.os.RemoteException; +import android.util.Slog; + +import java.util.Locale; /** * Represent a logical device of type TV residing in Android system. */ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { + private static final String TAG = "HdmiCecLocalDeviceTv"; HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiCec.DEVICE_TV); @@ -39,4 +47,67 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.launchDeviceDiscovery(mAddress); // TODO: Start routing control action, device discovery action. } + + @Override + protected boolean onMessage(HdmiCecMessage message) { + switch (message.getOpcode()) { + case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS: + return handleReportPhysicalAddress(message); + default: + return super.onMessage(message); + } + } + + /** + * Performs the action 'device select', or 'one touch play' initiated by TV. + * + * @param targetAddress logical address of the device to select + * @param callback callback object to report the result with + */ + void deviceSelect(int targetAddress, IHdmiControlCallback callback) { + HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress); + if (targetDevice == null) { + invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE); + return; + } + mService.removeAction(DeviceSelectAction.class); + mService.addAndStartAction(new DeviceSelectAction(mService, mAddress, + mService.getPhysicalAddress(), targetDevice, callback)); + } + + private static void invokeCallback(IHdmiControlCallback callback, int result) { + try { + callback.onComplete(result); + } catch (RemoteException e) { + Slog.e(TAG, "Invoking callback failed:" + e); + } + } + + @Override + protected boolean handleGetMenuLanguage(HdmiCecMessage message) { + HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand( + mAddress, Locale.getDefault().getISO3Language()); + // TODO: figure out how to handle failed to get language code. + if (command != null) { + mService.sendCecCommand(command); + } else { + Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString()); + } + return true; + } + + private boolean handleReportPhysicalAddress(HdmiCecMessage message) { + // Ignore if [Device Discovery Action] is going on. + if (mService.hasAction(DeviceDiscoveryAction.class)) { + Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> " + + "because Device Discovery Action is on-going:" + message); + return true; + } + + int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); + mService.addAndStartAction(new NewDeviceAction(mService, + mAddress, message.getSource(), physicalAddress)); + + return true; + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java index 6c2be34..8dbfd85 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -270,6 +270,38 @@ public class HdmiCecMessageBuilder { } /** + * Build <Set Stream Path> command. + * + * <p>This is a broadcast message sent to all devices on the bus. + * + * @param src source address of command + * @param streamPath physical address of the device to start streaming + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildSetStreamPath(int src, int streamPath) { + return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_SET_STREAM_PATH, + physicalAddressToParam(streamPath)); + } + + /** + * Build <Routing Change> command. + * + * <p>This is a broadcast message sent to all devices on the bus. + * + * @param src source address of command + * @param oldPath physical address of the currently active routing path + * @param newPath physical address of the new active routing path + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildRoutingChange(int src, int oldPath, int newPath) { + byte[] param = new byte[] { + (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF), + (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF) + }; + return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_ROUTING_CHANGE, param); + } + + /** * Build <Give Device Power Status> command. * * @param src source address of command @@ -319,10 +351,19 @@ public class HdmiCecMessageBuilder { * @return newly created {@link HdmiCecMessage} */ static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) { - byte[] params = new byte[] { - (byte) uiCommand - }; - return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, params); + return buildUserControlPressed(src, dest, new byte[] { (byte) uiCommand }); + } + + /** + * Build <User Control Pressed> command. + * + * @param src source address of command + * @param dest destination address of command + * @param commandParam uiCommand and the additional parameter + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildUserControlPressed(int src, int dest, byte[] commandParam) { + return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, commandParam); } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java index a83d1ed..8f319ea 100644 --- a/services/core/java/com/android/server/hdmi/HdmiConstants.java +++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java @@ -39,9 +39,41 @@ final class HdmiConstants { // Constants related to UI Command Codes. // Refer to CEC Table 30 in HDMI Spec v1.4b. + static final int UI_COMMAND_POWER = 0x40; static final int UI_COMMAND_MUTE = 0x43; static final int UI_COMMAND_MUTE_FUNCTION = 0x65; static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66; + static final int UI_COMMAND_POWER_ON_FUNCTION = 0x6D; + + // Bit mask used to get the routing path of the top level device. + // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0. + static final int ROUTING_PATH_TOP_MASK = 0xF000; + + // Flags used for setOption to CEC HAL. + /** + * When set to false, HAL does not wake up the system upon receiving + * <Image View On> or <Text View On>. Used when user changes the TV + * settings to disable the auto TV on functionality. + * True by default. + */ + static final int FLAG_HDMI_OPTION_WAKEUP = 1; + /** + * When set to false, all the CEC commands are discarded. Used when + * user changes the TV settings to disable CEC functionality. + * True by default. + */ + static final int FLAG_HDMI_OPTION_ENABLE_CEC = 2; + /** + * Setting this flag to false means Android system will stop handling + * CEC service and yield the control over to the microprocessor that is + * powered on through the standby mode. When set to true, the system + * will gain the control over, hence telling the microprocessor to stop + * handling the cec commands. This is called when system goes + * in and out of standby mode to notify the microprocessor that it should + * start/stop handling CEC commands on behalf of the system. + * False by default. + */ + static final int FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL = 3; private HdmiConstants() { /* cannot be instantiated */ } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 42cd654..0d7ac8d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -21,9 +21,11 @@ import android.content.Context; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; +import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlService; import android.hardware.hdmi.IHdmiHotplugEventListener; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -39,10 +41,10 @@ import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback; import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Locale; /** * Provides a service for sending and processing HDMI control messages, @@ -105,9 +107,8 @@ public final class HdmiControlService extends SystemService { // Used to synchronize the access to the service. private final Object mLock = new Object(); - // Type of logical devices hosted in the system. - @GuardedBy("mLock") - private final int[] mLocalDevices; + // Type of logical devices hosted in the system. Stored in the unmodifiable list. + private final List<Integer> mLocalDevices; // List of listeners registered by callers that want to get notified of // hotplug events. @@ -117,6 +118,9 @@ public final class HdmiControlService extends SystemService { private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords = new ArrayList<>(); + // Handler running on service thread. It's used to run a task in service thread. + private final Handler mHandler = new Handler(); + private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache(); @Nullable @@ -125,6 +129,25 @@ public final class HdmiControlService extends SystemService { @Nullable private HdmiMhlController mMhlController; + // HDMI port information. Stored in the unmodifiable list to keep the static information + // from being modified. + private List<HdmiPortInfo> mPortInfo; + + // Logical address of the active source. + @GuardedBy("mLock") + private int mActiveSource; + + // Active routing path. Physical address of the active source but not all the time, such as + // when the new active source does not claim itself to be one. + @GuardedBy("mLock") + private int mActiveRoutingPath; + + // Set to true while the service is in normal mode. While set to false, no input change is + // allowed. Used for situations where input change can confuse users such as channel auto-scan, + // system upgrade, etc., a.k.a. "prohibit mode". + @GuardedBy("mLock") + private boolean mInputChangeEnabled; + @GuardedBy("mLock") // Whether ARC is "enabled" or not. // TODO: it may need to hold lock if it's accessed from others. @@ -134,13 +157,12 @@ public final class HdmiControlService extends SystemService { // Whether SystemAudioMode is "On" or not. private boolean mSystemAudioMode; - // Handler running on service thread. It's used to run a task in service thread. - private final Handler mHandler = new Handler(); - public HdmiControlService(Context context) { super(context); - mLocalDevices = getContext().getResources().getIntArray( - com.android.internal.R.array.config_hdmiCecLogicalDeviceType); + mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray( + com.android.internal.R.array.config_hdmiCecLogicalDeviceType)); + // TODO: Get control flag from persistent storage + mInputChangeEnabled = true; } @Override @@ -158,14 +180,14 @@ public final class HdmiControlService extends SystemService { if (mMhlController == null) { Slog.i(TAG, "Device does not support MHL-control."); } - + mPortInfo = initPortInfo(); publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); // TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and // start to monitor the preference value and invoke SystemAudioActionFromTv if needed. } - private void initializeLocalDevices(final int[] deviceTypes) { + private void initializeLocalDevices(final List<Integer> deviceTypes) { // A container for [Logical Address, Local device info]. final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>(); final SparseIntArray finished = new SparseIntArray(); @@ -189,7 +211,7 @@ public final class HdmiControlService extends SystemService { // Once finish address allocation for all devices, notify // it to each device. - if (deviceTypes.length == finished.size()) { + if (deviceTypes.size() == finished.size()) { notifyAddressAllocated(devices); } } @@ -205,6 +227,66 @@ public final class HdmiControlService extends SystemService { } } + // Initialize HDMI port information. Combine the information from CEC and MHL HAL and + // keep them in one place. + private List<HdmiPortInfo> initPortInfo() { + HdmiPortInfo[] cecPortInfo = null; + + // CEC HAL provides majority of the info while MHL does only MHL support flag for + // each port. Return empty array if CEC HAL didn't provide the info. + if (mCecController != null) { + cecPortInfo = mCecController.getPortInfos(); + } + if (cecPortInfo == null) { + return Collections.emptyList(); + } + + HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0]; + if (mMhlController != null) { + // TODO: Implement plumbing logic to get MHL port information. + // mhlPortInfo = mMhlController.getPortInfos(); + } + + // Use the id (port number) to find the matched info between CEC and MHL to combine them + // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found. + ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length); + for (int i = 0; i < cecPortInfo.length; ++i) { + HdmiPortInfo cec = cecPortInfo[i]; + int id = cec.getId(); + boolean mhlInfoFound = false; + for (HdmiPortInfo mhl : mhlPortInfo) { + if (id == mhl.getId()) { + result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(), + cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported())); + mhlInfoFound = true; + break; + } + } + if (!mhlInfoFound) { + result.add(cec); + } + } + + return Collections.unmodifiableList(result); + } + + /** + * Returns HDMI port information for the given port id. + * + * @param portId HDMI port id + * @return {@link HdmiPortInfo} for the given port + */ + HdmiPortInfo getPortInfo(int portId) { + // mPortInfo is an unmodifiable list and the only reference to its inner list. + // No lock is necessary. + for (HdmiPortInfo info : mPortInfo) { + if (portId == info.getId()) { + return info; + } + } + return null; + } + /** * Returns {@link Looper} for IO operation. * @@ -224,6 +306,41 @@ public final class HdmiControlService extends SystemService { return mHandler.getLooper(); } + int getActiveSource() { + synchronized (mLock) { + return mActiveSource; + } + } + + int getActivePath() { + synchronized (mLock) { + return mActiveRoutingPath; + } + } + + /** + * Returns the path (physical address) of the device at the top of the currently active + * routing path. Used to get the corresponding port address of the HDMI input of the TV. + */ + int getActiveInput() { + synchronized (mLock) { + return mActiveRoutingPath & HdmiConstants.ROUTING_PATH_TOP_MASK; + } + } + + void updateActiveDevice(int logicalAddress, int physicalAddress) { + synchronized (mLock) { + mActiveSource = logicalAddress; + mActiveRoutingPath = physicalAddress; + } + } + + void setInputChangeEnabled(boolean enabled) { + synchronized (mLock) { + mInputChangeEnabled = enabled; + } + } + /** * Returns physical address of the device. */ @@ -238,6 +355,18 @@ public final class HdmiControlService extends SystemService { return mCecController.getVendorId(); } + HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) { + assertRunOnServiceThread(); + return mCecController.getDeviceInfo(logicalAddress); + } + + /** + * Returns version of CEC. + */ + int getCecVersion() { + return mCecController.getVersion(); + } + /** * Returns a list of {@link HdmiCecDeviceInfo}. * @@ -249,6 +378,24 @@ public final class HdmiControlService extends SystemService { } /** + * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches + * the given routing path. CEC devices use routing path for its physical address to + * describe the hierarchy of the devices in the network. + * + * @param path routing path or physical address + * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null + */ + HdmiCecDeviceInfo getDeviceInfoByPath(int path) { + assertRunOnServiceThread(); + for (HdmiCecDeviceInfo info : mCecController.getDeviceInfoList(false)) { + if (info.getPhysicalAddress() == path) { + return info; + } + } + return null; + } + + /** * Add and start a new {@link FeatureAction} to the action queue. * * @param action {@link FeatureAction} to add and start @@ -277,7 +424,7 @@ public final class HdmiControlService extends SystemService { } // See if we have an action of a given type in progress. - private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) { + <T extends FeatureAction> boolean hasAction(final Class<T> clazz) { for (FeatureAction action : mActions) { if (action.getClass().equals(clazz)) { return true; @@ -297,7 +444,7 @@ public final class HdmiControlService extends SystemService { } // Remove all actions matched with the given Class type. - private <T extends FeatureAction> void removeAction(final Class<T> clazz) { + <T extends FeatureAction> void removeAction(final Class<T> clazz) { removeActionExcept(clazz, null); } @@ -335,10 +482,13 @@ public final class HdmiControlService extends SystemService { * @return {@code true} if ARC was in "Enabled" status */ boolean setArcStatus(boolean enabled) { + assertRunOnServiceThread(); synchronized (mLock) { boolean oldStatus = mArcStatusEnabled; // 1. Enable/disable ARC circuit. - // TODO: call set_audio_return_channel of hal interface. + mCecController.setAudioReturnChannel(enabled); + + // TODO: notify arc mode change to AudioManager. // 2. Update arc status; mArcStatusEnabled = enabled; @@ -360,46 +510,20 @@ public final class HdmiControlService extends SystemService { mCecController.sendCommand(command, null); } - /** - * Add a new {@link HdmiCecDeviceInfo} to controller. - * - * @param deviceInfo new device information object to add - */ - void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { - // TODO: Implement this. - } - boolean handleCecCommand(HdmiCecMessage message) { // Cache incoming message. Note that it caches only white-listed one. mCecMessageCache.cacheMessage(message); // Commands that queries system information replies directly instead // of creating FeatureAction because they are state-less. + // TODO: move the leftover message to local device. switch (message.getOpcode()) { - case HdmiCec.MESSAGE_GET_MENU_LANGUAGE: - handleGetMenuLanguage(message); - return true; - case HdmiCec.MESSAGE_GIVE_OSD_NAME: - handleGiveOsdName(message); - return true; - case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS: - handleGivePhysicalAddress(message); - return true; - case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID: - handleGiveDeviceVendorId(message); - return true; - case HdmiCec.MESSAGE_GET_CEC_VERSION: - handleGetCecVersion(message); - return true; case HdmiCec.MESSAGE_INITIATE_ARC: handleInitiateArc(message); return true; case HdmiCec.MESSAGE_TERMINATE_ARC: handleTerminateArc(message); return true; - case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS: - handleReportPhysicalAddress(message); - return true; case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE: handleSetSystemAudioMode(message); return true; @@ -407,8 +531,22 @@ public final class HdmiControlService extends SystemService { handleSystemAudioModeStatus(message); return true; default: - return dispatchMessageToAction(message); + if (dispatchMessageToAction(message)) { + return true; + } + break; + } + + return dispatchMessageToLocalDevice(message); + } + + private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) { + for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { + if (device.dispatchMessage(message)) { + return true; + } } + return false; } /** @@ -455,20 +593,19 @@ public final class HdmiControlService extends SystemService { void launchDeviceDiscovery(final int sourceAddress) { // At first, clear all existing device infos. mCecController.clearDeviceInfoList(); - mCecMessageCache.flushAll(); + // TODO: flush cec message cache when CEC is turned off. - // TODO: check whether TV is one of local devices. DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress, new DeviceDiscoveryCallback() { @Override public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) { for (HdmiCecDeviceInfo info : deviceInfos) { - mCecController.addDeviceInfo(info); + addCecDevice(info); } // Add device info of all local devices. for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { - mCecController.addDeviceInfo(device.getDeviceInfo()); + addCecDevice(device.getDeviceInfo()); } addAndStartAction(new HotplugDetectionAction(HdmiControlService.this, @@ -479,28 +616,12 @@ public final class HdmiControlService extends SystemService { } private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) { - // TODO: get device name read from system configuration. - String displayName = HdmiCec.getDefaultDeviceName(logicalAddress); + // TODO: find better name instead of model name. + String displayName = Build.MODEL; return new HdmiCecDeviceInfo(logicalAddress, getPhysicalAddress(), deviceType, getVendorId(), displayName); } - private void handleReportPhysicalAddress(HdmiCecMessage message) { - // At first, try to consume it. - if (dispatchMessageToAction(message)) { - return; - } - - // Ignore if [Device Discovery Action] is going on. - if (hasAction(DeviceDiscoveryAction.class)) { - Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> " - + "because Device Discovery Action is on-going:" + message); - return; - } - - // TODO: start new device action. - } - private void handleInitiateArc(HdmiCecMessage message){ // In case where <Initiate Arc> is started by <Request ARC Initiation> // need to clean up RequestArcInitiationAction. @@ -521,64 +642,6 @@ public final class HdmiControlService extends SystemService { addAndStartAction(action); } - private void handleGetCecVersion(HdmiCecMessage message) { - int version = mCecController.getVersion(); - HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(), - message.getSource(), - version); - sendCecCommand(cecMessage); - } - - private void handleGiveDeviceVendorId(HdmiCecMessage message) { - int vendorId = mCecController.getVendorId(); - HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand( - message.getDestination(), vendorId); - sendCecCommand(cecMessage); - } - - private void handleGivePhysicalAddress(HdmiCecMessage message) { - int physicalAddress = mCecController.getPhysicalAddress(); - int deviceType = HdmiCec.getTypeFromAddress(message.getDestination()); - HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( - message.getDestination(), physicalAddress, deviceType); - sendCecCommand(cecMessage); - } - - private void handleGiveOsdName(HdmiCecMessage message) { - // TODO: read device name from settings or property. - String name = HdmiCec.getDefaultDeviceName(message.getDestination()); - HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand( - message.getDestination(), message.getSource(), name); - if (cecMessage != null) { - sendCecCommand(cecMessage); - } else { - Slog.w(TAG, "Failed to build <Get Osd Name>:" + name); - } - } - - private void handleGetMenuLanguage(HdmiCecMessage message) { - // Only 0 (TV), 14 (specific use) can answer. - if (message.getDestination() != HdmiCec.ADDR_TV - && message.getDestination() != HdmiCec.ADDR_SPECIFIC_USE) { - Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString()); - sendCecCommand( - HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(), - message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE, - HdmiConstants.ABORT_UNRECOGNIZED_MODE)); - return; - } - - HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand( - message.getDestination(), - Locale.getDefault().getISO3Language()); - // TODO: figure out how to handle failed to get language code. - if (command != null) { - sendCecCommand(command); - } else { - Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString()); - } - } - private boolean dispatchMessageToAction(HdmiCecMessage message) { for (FeatureAction action : mActions) { if (action.processCommand(message)) { @@ -646,11 +709,33 @@ public final class HdmiControlService extends SystemService { @Override public int[] getSupportedTypes() { enforceAccessPermission(); - synchronized (mLock) { - return mLocalDevices; + // mLocalDevices is an unmodifiable list - no lock necesary. + int[] localDevices = new int[mLocalDevices.size()]; + for (int i = 0; i < localDevices.length; ++i) { + localDevices[i] = mLocalDevices.get(i); } + return localDevices; + } + + @Override + public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiCecLocalDeviceTv tv = + (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV); + if (tv == null) { + Slog.w(TAG, "Local playback device not available"); + invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE); + return; + } + tv.deviceSelect(logicalAddress, callback); + } + }); } + @Override public void oneTouchPlay(final IHdmiControlCallback callback) { enforceAccessPermission(); @@ -787,8 +872,9 @@ public final class HdmiControlService extends SystemService { } boolean isInPresetInstallationMode() { - // TODO: Implement this. - return false; + synchronized (mLock) { + return !mInputChangeEnabled; + } } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index ef128ed1..b534377 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -20,6 +20,10 @@ import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; import android.util.Slog; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * Various utilities to handle HDMI CEC messages. */ @@ -71,4 +75,40 @@ final class HdmiUtils { return cmd.getParams().length > 0 && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON; } + + /** + * Convert integer array to list of {@link Integer}. + * + * <p>The result is immutable. + * + * @param is integer array + * @return {@link List} instance containing the elements in the given array + */ + static List<Integer> asImmutableList(final int[] is) { + ArrayList<Integer> list = new ArrayList<>(is.length); + for (int type : is) { + list.add(type); + } + return Collections.unmodifiableList(list); + } + + /** + * Assemble two bytes into single integer value. + * + * @param data to be assembled + * @return assembled value + */ + static int twoBytesToInt(byte[] data) { + return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF); + } + + /** + * Assemble three bytes into single integer value. + * + * @param data to be assembled + * @return assembled value + */ + static int threeBytesToInt(byte[] data) { + return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); + } } diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java index 3e518ea..c7a813d 100644 --- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java +++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java @@ -122,7 +122,7 @@ final class HotplugDetectionAction extends FeatureAction { mService.pollDevices(new DevicePollingCallback() { @Override public void onPollingFinished(List<Integer> ackedAddress) { - checkHotplug(ackedAddress, false); + checkHotplug(ackedAddress, true); } }, HdmiControlService.POLL_ITERATION_IN_ORDER | HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO, POLL_RETRY_COUNT); diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index 156bbbe..c284d10 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -70,10 +70,13 @@ final class NewDeviceAction extends FeatureAction { @Override public boolean start() { - sendCommand( - HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, - mDeviceLogicalAddress)); mState = STATE_WAITING_FOR_SET_OSD_NAME; + if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_SET_OSD_NAME)) { + return true; + } + + sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, + mDeviceLogicalAddress)); addTimer(mState, TIMEOUT_MS); return true; } @@ -99,13 +102,11 @@ final class NewDeviceAction extends FeatureAction { } catch (UnsupportedEncodingException e) { Slog.e(TAG, "Failed to get OSD name: " + e.getMessage()); } - mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); return true; } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) { - int requestOpcode = params[1] & 0xff; + int requestOpcode = params[1] & 0xFF; if (requestOpcode == HdmiCec.MESSAGE_SET_OSD_NAME) { - mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); return true; } @@ -113,8 +114,7 @@ final class NewDeviceAction extends FeatureAction { } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { if (params.length == 3) { - mVendorId = ((params[0] & 0xff) << 16) + ((params[1] & 0xff) << 8) - + (params[2] & 0xff); + mVendorId = HdmiUtils.threeBytesToInt(params); } else { Slog.e(TAG, "Failed to get device vendor ID: "); } @@ -122,7 +122,7 @@ final class NewDeviceAction extends FeatureAction { finish(); return true; } else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) { - int requestOpcode = params[1] & 0xff; + int requestOpcode = params[1] & 0xFF; if (requestOpcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { addDeviceInfo(); finish(); @@ -133,7 +133,21 @@ final class NewDeviceAction extends FeatureAction { return false; } + private boolean mayProcessCommandIfCached(int destAddress, int opcode) { + HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode); + if (message != null) { + return processCommand(message); + } + return false; + } + private void requestVendorId() { + // At first, transit to waiting status for <Device Vendor Id>. + mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; + // If the message is already in cache, process it. + if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) { + return; + } sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, mDeviceLogicalAddress)); addTimer(mState, TIMEOUT_MS); @@ -143,7 +157,7 @@ final class NewDeviceAction extends FeatureAction { if (mDisplayName == null) { mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress); } - mService.addDeviceInfo(new HdmiCecDeviceInfo( + mService.addCecDevice(new HdmiCecDeviceInfo( mDeviceLogicalAddress, mDevicePhysicalAddress, HdmiCec.getTypeFromAddress(mDeviceLogicalAddress), mVendorId, mDisplayName)); @@ -156,7 +170,6 @@ final class NewDeviceAction extends FeatureAction { } if (state == STATE_WAITING_FOR_SET_OSD_NAME) { // Osd name request timed out. Try vendor id - mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID; requestVendorId(); } else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { // vendor id timed out. Go ahead creating the device info what we've got so far. diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java new file mode 100644 index 0000000..26048d2 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2014 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.hdmi; + +import java.util.concurrent.TimeUnit; + +import android.hardware.hdmi.IHdmiControlCallback; +import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.hdmi.HdmiControlService.SendMessageCallback; + +/** + * Feature action for routing control. Exchanges routing-related commands with other devices + * to determine the new active source. + * + * <p>This action is initiated by various cases: + * <ul> + * <li> Manual TV input switching + * <li> Routing change of a CEC switch other than TV + * <li> New CEC device at the tail of the active routing path + * <li> Removed CEC device from the active routing path + * <li> Routing at CEC enable time + * </ul> + */ +public class RoutingControlAction extends FeatureAction { + private static final String TAG = "RoutingControlAction"; + + // State in which we wait for <Routing Information> to arrive. If timed out, we use the + // latest routing path to set the new active source. + private final static int STATE_WAIT_FOR_ROUTING_INFORMATION = 1; + + // State in which we wait for <Report Power Status> in response to <Give Device Power Status> + // we have sent. If the response tells us the device power is on, we send <Set Stream Path> + // to make it the active source. Otherwise we do not send <Set Stream Path>, and possibly + // just show the blank screen. + private final static int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2; + + // Time out in millseconds used for <Routing Information> + private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000; + + // Time out in milliseconds used for <Report Power Status> + private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000; + + private final IHdmiControlCallback mCallback; + + // The latest routing path. Updated by each <Routing Information> from CEC switches. + private int mCurrentRoutingPath; + + RoutingControlAction(HdmiControlService service, int sourceAddress, int portId, + IHdmiControlCallback callback) { + super(service, sourceAddress); + mCallback = callback; + mCurrentRoutingPath = portToPath(portId); + } + + @Override + public boolean start() { + mState = STATE_WAIT_FOR_ROUTING_INFORMATION; + addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS); + return true; + } + + @Override + public boolean processCommand(HdmiCecMessage cmd) { + int opcode = cmd.getOpcode(); + byte[] params = cmd.getParams(); + if (mState == STATE_WAIT_FOR_ROUTING_INFORMATION + && opcode == HdmiCec.MESSAGE_ROUTING_INFORMATION) { + // Keep updating the physicalAddress as we receive <Routing Information>. + // If the routing path doesn't belong to the currently active one, we should + // ignore it since it might have come from other, routing change sequence. + int routingPath = HdmiUtils.twoBytesToInt(params); + if (isInActiveRoutingPath(mCurrentRoutingPath, routingPath)) { + return true; + } + mCurrentRoutingPath = routingPath; + // Stop possible previous routing change sequence if in progress. + mService.removeAction(RoutingControlAction.class); + addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS); + return true; + } else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS + && opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS) { + handleReportPowerStatus(cmd.getParams()[0]); + return true; + } + return false; + } + + private void handleReportPowerStatus(int devicePowerStatus) { + int tvPowerStatus = getTvPowerStatus(); + if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) { + if (isPowerStatusOnOrTransientToOn(devicePowerStatus)) { + sendSetStreamPath(); + } else { + // The whole action should be stopped here if the device is in standby mode. + // We don't attempt to wake it up by sending <Set Stream Path>. + } + invokeCallback(HdmiCec.RESULT_SUCCESS); + finish(); + } else { + // TV is going into standby mode. + // TODO: Figure out what to do. + } + } + + private int getTvPowerStatus() { + // TODO: Obtain TV power status. + return HdmiCec.POWER_STATUS_ON; + } + + private static boolean isPowerStatusOnOrTransientToOn(int status) { + return status == HdmiCec.POWER_STATUS_ON || status == HdmiCec.POWER_STATUS_TRANSIENT_TO_ON; + } + + private void sendSetStreamPath() { + sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(mSourceAddress, mCurrentRoutingPath)); + } + + private static boolean isInActiveRoutingPath(int activePath, int newPath) { + // Check each nibble of the currently active path and the new path till the position + // where the active nibble is not zero. For (activePath, newPath), + // (1.1.0.0, 1.0.0.0) -> true, new path is a parent + // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant + // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling + // (1.0.0.0, 2.0.0.0) -> false, in a completely different path + for (int i = 12; i >= 0; i -= 4) { + int nibbleActive = (activePath >> i) & 0xF; + if (nibbleActive == 0) { + break; + } + int nibbleNew = (newPath >> i) & 0xF; + if (nibbleNew == 0) { + break; + } + if (nibbleActive != nibbleNew) { + return false; + } + } + return true; + } + + @Override + public void handleTimerEvent(int timeoutState) { + if (mState != timeoutState || mState == STATE_NONE) { + Slog.w("CEC", "Timer in a wrong state. Ignored."); + return; + } + switch (timeoutState) { + case STATE_WAIT_FOR_ROUTING_INFORMATION: + HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath); + if (device == null) { + maybeChangeActiveInput(pathToPort(mCurrentRoutingPath)); + } else { + // TODO: Also check followings and then proceed: + // if routing change was neither triggered by TV at CEC enable time, nor + // at the detection of new device at the end of the active routing path, nor + // by TV power on with HDMI input as the active signal source. + int deviceLogicalAddress = device.getLogicalAddress(); + queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() { + @Override + public void onSendCompleted(int error) { + handlDevicePowerStatusAckResult(error == HdmiCec.RESULT_SUCCESS); + } + }); + } + return; + case STATE_WAIT_FOR_REPORT_POWER_STATUS: + int tvPowerStatus = getTvPowerStatus(); + if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) { + if (!maybeChangeActiveInput(pathToPort(mCurrentRoutingPath))) { + sendSetStreamPath(); + } + } + invokeCallback(HdmiCec.RESULT_SUCCESS); + finish(); + return; + } + } + + // Called whenever an HDMI input of the TV shall become the active input. + private boolean maybeChangeActiveInput(int inputPortPath) { + if (mService.getActiveInput() == inputPortPath) { + return false; + } + // TODO: Remember the currently active input + // if PAP/PIP is active, move the focus to the right window, otherwise switch + // the port. + // Show the OSD input change banner. + return true; + } + + private void queryDevicePowerStatus(int address, SendMessageCallback callback) { + sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address), + callback); + } + + private void handlDevicePowerStatusAckResult(boolean acked) { + if (acked) { + mState = STATE_WAIT_FOR_REPORT_POWER_STATUS; + addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS); + } else { + maybeChangeActiveInput(pathToPort(mCurrentRoutingPath)); + } + } + + // Get the address of the TV port to which the given path is connected. + private static int pathToPort(int path) { + return path & HdmiConstants.ROUTING_PATH_TOP_MASK; + } + + // Given the HDMI port id, return the port address. + private int portToPath(int portId) { + return mService.getPortInfo(portId).getAddress(); + } + + private void invokeCallback(int result) { + if (mCallback == null) { + return; + } + try { + mCallback.onComplete(result); + } catch (RemoteException e) { + // Do nothing. + } + } +} diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java new file mode 100644 index 0000000..5eb9315 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2014 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.hdmi; + +import android.hardware.hdmi.HdmiCecMessage; +import android.view.KeyEvent; +import android.util.Slog; + +import libcore.util.EmptyArray; + +/** + * Feature action that transmits remote control key command (User Control Press/ + * User Control Release) to CEC bus. + * + * <p>This action is created when a new key event is passed to CEC service. It optionally + * does key repeat (a.k.a. press-and-hold) operation until it receives a key release event. + * If another key press event is received before the key in use is released, CEC service + * does not create a new action but recycles the current one by updating the key used + * for press-and-hold operation. + * + * <p>Package-private, accessed by {@link HdmiControlService} only. + */ +final class SendKeyAction extends FeatureAction { + private static final String TAG = "SendKeyAction"; + + // State in which the action is at work. The state is set in {@link #start()} and + // persists throughout the process till it is set back to {@code STATE_NONE} at the end. + private static final int STATE_PROCESSING_KEYCODE = 1; + + // IRT(Initiator Repetition Time) in millisecond as recommended in the standard. + // Outgoing UCP commands, when in 'Press and Hold' mode, should be this much apart + // from the adjacent one so as not to place unnecessarily heavy load on the CEC line. + // TODO: This value might need tweaking per product basis. Consider putting it + // in config.xml to allow customization. + private static final int IRT_MS = 450; + + // Logical address of the device to which the UCP/UCP commands are sent. + private final int mTargetAddress; + + // The key code of the last key press event the action is passed via processKeyEvent. + private int mLastKeyCode; + + /** + * Constructor. + * + * @param service {@link HdmiControlService} instance + * @param sourceAddress logical address to be used as source address + * @param targetAddress logical address of the device to send the keys to + * @param keyCode remote control key code as defined in {@link KeyEvent} + */ + SendKeyAction(HdmiControlService service, int sourceAddress, int targetAddress, int keyCode) { + super(service, sourceAddress); + mTargetAddress = targetAddress; + mLastKeyCode = keyCode; + } + + @Override + public boolean start() { + sendKeyDown(mLastKeyCode); + mState = STATE_PROCESSING_KEYCODE; + addTimer(mState, IRT_MS); + return true; + } + + /** + * Called when a key event should be handled for the action. + * + * @param keyCode key code of {@link KeyEvent} object + * @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN} + * @param param additional parameter that comes with the key event + */ + void processKeyEvent(int keyCode, boolean isPressed) { + if (mState != STATE_PROCESSING_KEYCODE) { + Slog.w(TAG, "Not in a valid state"); + return; + } + // A new key press event that comes in with a key code different from the last + // one sets becomes a new key code to be used for press-and-hold operation. + // Removes any pending timer and starts a new timer for itself. + // Key release event indicates that the action shall be finished. Send UCR + // command and terminate the action. Other release events are ignored. + if (isPressed) { + if (keyCode != mLastKeyCode) { + mActionTimer.clearTimerMessage(); + sendKeyDown(keyCode); + addTimer(mState, IRT_MS); + mLastKeyCode = keyCode; + } + } else { + if (keyCode == mLastKeyCode) { + sendKeyUp(); + finish(); + } + } + } + + private void sendKeyDown(int keyCode) { + byte[] keyCodeAndParam = getCecKeyCodeAndParam(keyCode); + if (keyCodeAndParam == null) { + return; + } + sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, mTargetAddress, + keyCodeAndParam)); + } + + private void sendKeyUp() { + sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, mTargetAddress)); + } + + @Override + public boolean processCommand(HdmiCecMessage cmd) { + // Send key action doesn't need any incoming CEC command, hence does not consume it. + return false; + } + + @Override + public void handleTimerEvent(int state) { + // Timer event occurs every IRT_MS milliseconds to perform key-repeat (or press-and-hold) + // operation. If the last received key code is as same as the one with which the action + // is started, plus there was no key release event in last IRT_MS timeframe, send a UCP + // command and start another timer to schedule the next press-and-hold command. + if (mState != STATE_PROCESSING_KEYCODE) { + Slog.w(TAG, "Not in a valid state"); + return; + } + sendKeyDown(mLastKeyCode); + addTimer(mState, IRT_MS); + } + + // Converts the Android key code to corresponding CEC key code definition. Those CEC keys + // with additional parameters should be mapped from individual Android key code. 'Select + // Broadcast' with the parameter 'cable', for instance, shall have its counterpart such as + // KeyEvent.KEYCODE_TV_BROADCAST_CABLE. + // The return byte array contains both UI command (keycode) and optional parameter. + private byte[] getCecKeyCodeAndParam(int keyCode) { + // TODO: Convert to CEC keycode and (optionally) parameter. + // return androidKeyToCecKey(keyCode); + return EmptyArray.BYTE; + } +} diff --git a/services/core/java/com/android/server/task/TaskCompletedListener.java b/services/core/java/com/android/server/job/JobCompletedListener.java index c53f5ca..a7af9cd 100644 --- a/services/core/java/com/android/server/task/TaskCompletedListener.java +++ b/services/core/java/com/android/server/job/JobCompletedListener.java @@ -14,19 +14,19 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; /** - * Used for communication between {@link com.android.server.task.TaskServiceContext} and the - * {@link com.android.server.task.TaskManagerService}. + * Used for communication between {@link com.android.server.job.JobServiceContext} and the + * {@link com.android.server.job.JobSchedulerService}. */ -public interface TaskCompletedListener { +public interface JobCompletedListener { /** - * Callback for when a task is completed. - * @param needsReschedule Whether the implementing class should reschedule this task. + * Callback for when a job is completed. + * @param needsReschedule Whether the implementing class should reschedule this job. */ - public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule); + public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule); } diff --git a/services/core/java/com/android/server/task/TaskMapReadFinishedListener.java b/services/core/java/com/android/server/job/JobMapReadFinishedListener.java index c68d8db..f3e77e6 100644 --- a/services/core/java/com/android/server/task/TaskMapReadFinishedListener.java +++ b/services/core/java/com/android/server/job/JobMapReadFinishedListener.java @@ -14,21 +14,21 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; import java.util.List; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; /** - * Callback definition for I/O thread to let the TaskManagerService know when + * Callback definition for I/O thread to let the JobManagerService know when * I/O read has completed. Done this way so we don't stall the main thread on * boot. */ -public interface TaskMapReadFinishedListener { +public interface JobMapReadFinishedListener { /** - * Called by the {@link TaskStore} at boot, when the disk read is finished. + * Called by the {@link JobStore} at boot, when the disk read is finished. */ - public void onTaskMapReadFinished(List<TaskStatus> tasks); + public void onJobMapReadFinished(List<JobStatus> jobs); } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java new file mode 100644 index 0000000..0e9a9cc --- /dev/null +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -0,0 +1,764 @@ +/* + * Copyright (C) 2014 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.job; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.app.job.IJobScheduler; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.server.job.controllers.BatteryController; +import com.android.server.job.controllers.ConnectivityController; +import com.android.server.job.controllers.IdleController; +import com.android.server.job.controllers.JobStatus; +import com.android.server.job.controllers.StateController; +import com.android.server.job.controllers.TimeController; + +import java.util.LinkedList; + +/** + * Responsible for taking jobs representing work to be performed by a client app, and determining + * based on the criteria specified when that job should be run against the client application's + * endpoint. + * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing + * about constraints, or the state of active jobs. It receives callbacks from the various + * controllers and completed jobs and operates accordingly. + * + * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object. + * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}. + * @hide + */ +public class JobSchedulerService extends com.android.server.SystemService + implements StateChangedListener, JobCompletedListener, JobMapReadFinishedListener { + // TODO: Switch this off for final version. + static final boolean DEBUG = true; + /** The number of concurrent jobs we run at one time. */ + private static final int MAX_JOB_CONTEXTS_COUNT = 3; + static final String TAG = "JobManagerService"; + /** Master list of jobs. */ + private final JobStore mJobs; + + static final int MSG_JOB_EXPIRED = 0; + static final int MSG_CHECK_JOB = 1; + + // Policy constants + /** + * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things + * early. + */ + private static final int MIN_IDLE_COUNT = 1; + /** + * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule + * things early. + */ + private static final int MIN_CONNECTIVITY_COUNT = 2; + /** + * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running + * some work early. + */ + private static final int MIN_READY_JOBS_COUNT = 4; + + /** + * Track Services that have currently active or pending jobs. The index is provided by + * {@link JobStatus#getServiceToken()} + */ + private final List<JobServiceContext> mActiveServices = new LinkedList<JobServiceContext>(); + /** List of controllers that will notify this service of updates to jobs. */ + private List<StateController> mControllers; + /** + * Queue of pending jobs. The JobServiceContext class will receive jobs from this list + * when ready to execute them. + */ + private final LinkedList<JobStatus> mPendingJobs = new LinkedList<JobStatus>(); + + private final JobHandler mHandler; + private final JobSchedulerStub mJobSchedulerStub; + /** + * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we + * still clean up. On reinstall the package will have a new uid. + */ + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Slog.d(TAG, "Receieved: " + intent.getAction()); + if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { + int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (DEBUG) { + Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); + } + cancelJobsForUid(uidRemoved); + } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); + if (DEBUG) { + Slog.d(TAG, "Removing jobs for user: " + userId); + } + cancelJobsForUser(userId); + } + } + }; + + /** + * Entry point from client to schedule the provided job. + * This cancels the job if it's already been scheduled, and replaces it with the one provided. + * @param job JobInfo object containing execution parameters + * @param uId The package identifier of the application this job is for. + * @param canPersistJob Whether or not the client has the appropriate permissions for + * persisting this job. + * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes. + */ + public int schedule(JobInfo job, int uId, boolean canPersistJob) { + JobStatus jobStatus = new JobStatus(job, uId, canPersistJob); + cancelJob(uId, job.getId()); + startTrackingJob(jobStatus); + return JobScheduler.RESULT_SUCCESS; + } + + public List<JobInfo> getPendingJobs(int uid) { + ArrayList<JobInfo> outList = new ArrayList<JobInfo>(); + synchronized (mJobs) { + for (JobStatus job : mJobs.getJobs()) { + if (job.getUid() == uid) { + outList.add(job.getJob()); + } + } + } + return outList; + } + + private void cancelJobsForUser(int userHandle) { + synchronized (mJobs) { + List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle); + for (JobStatus toRemove : jobsForUser) { + if (DEBUG) { + Slog.d(TAG, "Cancelling: " + toRemove); + } + cancelJobLocked(toRemove); + } + } + } + + /** + * Entry point from client to cancel all jobs originating from their uid. + * This will remove the job from the master list, and cancel the job if it was staged for + * execution or being executed. + * @param uid To check against for removal of a job. + */ + public void cancelJobsForUid(int uid) { + // Remove from master list. + synchronized (mJobs) { + List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); + for (JobStatus toRemove : jobsForUid) { + if (DEBUG) { + Slog.d(TAG, "Cancelling: " + toRemove); + } + cancelJobLocked(toRemove); + } + } + } + + /** + * Entry point from client to cancel the job corresponding to the jobId provided. + * This will remove the job from the master list, and cancel the job if it was staged for + * execution or being executed. + * @param uid Uid of the calling client. + * @param jobId Id of the job, provided at schedule-time. + */ + public void cancelJob(int uid, int jobId) { + JobStatus toCancel; + synchronized (mJobs) { + toCancel = mJobs.getJobByUidAndJobId(uid, jobId); + if (toCancel != null) { + cancelJobLocked(toCancel); + } + } + } + + private void cancelJobLocked(JobStatus cancelled) { + // Remove from store. + stopTrackingJob(cancelled); + // Remove from pending queue. + mPendingJobs.remove(cancelled); + // Cancel if running. + stopJobOnServiceContextLocked(cancelled); + } + + /** + * Initializes the system service. + * <p> + * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + * </p> + * + * @param context The system server context. + */ + public JobSchedulerService(Context context) { + super(context); + // Create the controllers. + mControllers = new LinkedList<StateController>(); + mControllers.add(ConnectivityController.get(this)); + mControllers.add(TimeController.get(this)); + mControllers.add(IdleController.get(this)); + mControllers.add(BatteryController.get(this)); + + mHandler = new JobHandler(context.getMainLooper()); + mJobSchedulerStub = new JobSchedulerStub(); + // Create the "runners". + for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) { + mActiveServices.add( + new JobServiceContext(this, context.getMainLooper())); + } + mJobs = JobStore.initAndGet(this); + } + + @Override + public void onStart() { + publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub); + } + + @Override + 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); + filter.addDataScheme("package"); + getContext().registerReceiverAsUser( + mBroadcastReceiver, UserHandle.ALL, filter, null, null); + final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); + getContext().registerReceiverAsUser( + mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); + } + } + + /** + * Called when we have a job status object that we need to insert in our + * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know + * about. + */ + private void startTrackingJob(JobStatus jobStatus) { + boolean update; + synchronized (mJobs) { + update = mJobs.add(jobStatus); + } + for (StateController controller : mControllers) { + if (update) { + controller.maybeStopTrackingJob(jobStatus); + } + controller.maybeStartTrackingJob(jobStatus); + } + } + + /** + * Called when we want to remove a JobStatus object that we've finished executing. Returns the + * object removed. + */ + private boolean stopTrackingJob(JobStatus jobStatus) { + boolean removed; + synchronized (mJobs) { + // Remove from store as well as controllers. + removed = mJobs.remove(jobStatus); + } + if (removed) { + for (StateController controller : mControllers) { + controller.maybeStopTrackingJob(jobStatus); + } + } + return removed; + } + + private boolean stopJobOnServiceContextLocked(JobStatus job) { + for (JobServiceContext jsc : mActiveServices) { + final JobStatus executing = jsc.getRunningJob(); + if (executing != null && executing.matches(job.getUid(), job.getJobId())) { + jsc.cancelExecutingJob(); + return true; + } + } + return false; + } + + /** + * @param job JobStatus we are querying against. + * @return Whether or not the job represented by the status object is currently being run or + * is pending. + */ + private boolean isCurrentlyActiveLocked(JobStatus job) { + for (JobServiceContext serviceContext : mActiveServices) { + final JobStatus running = serviceContext.getRunningJob(); + if (running != null && running.matches(job.getUid(), job.getJobId())) { + return true; + } + } + return false; + } + + /** + * A job is rescheduled with exponential back-off if the client requests this from their + * execution logic. + * A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the + * timeliness of the reschedule. For an idle-mode job, no deadline is given. + * @param failureToReschedule Provided job status that we will reschedule. + * @return A newly instantiated JobStatus with the same constraints as the last job except + * with adjusted timing constraints. + */ + private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { + final long elapsedNowMillis = SystemClock.elapsedRealtime(); + final JobInfo job = failureToReschedule.getJob(); + + final long initialBackoffMillis = job.getInitialBackoffMillis(); + final int backoffAttempt = failureToReschedule.getNumFailures() + 1; + long newEarliestRuntimeElapsed = elapsedNowMillis; + + switch (job.getBackoffPolicy()) { + case JobInfo.BackoffPolicy.LINEAR: + newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt; + break; + default: + if (DEBUG) { + Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); + } + case JobInfo.BackoffPolicy.EXPONENTIAL: + newEarliestRuntimeElapsed += + Math.pow(initialBackoffMillis * 0.001, backoffAttempt) * 1000; + break; + } + newEarliestRuntimeElapsed = + Math.min(newEarliestRuntimeElapsed, JobInfo.MAX_BACKOFF_DELAY_MILLIS); + return new JobStatus(failureToReschedule, newEarliestRuntimeElapsed, + JobStatus.NO_LATEST_RUNTIME, backoffAttempt); + } + + /** + * Called after a periodic has executed so we can to re-add it. We take the last execution time + * of the job to be the time of completion (i.e. the time at which this function is called). + * This could be inaccurate b/c the job can run for as long as + * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead + * to underscheduling at least, rather than if we had taken the last execution time to be the + * start of the execution. + * @return A new job representing the execution criteria for this instantiation of the + * recurring job. + */ + private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { + final long elapsedNow = SystemClock.elapsedRealtime(); + // Compute how much of the period is remaining. + long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0); + long newEarliestRunTimeElapsed = elapsedNow + runEarly; + long period = periodicToReschedule.getJob().getIntervalMillis(); + long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period; + + if (DEBUG) { + Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + + newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); + } + return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed, + newLatestRuntimeElapsed, 0 /* backoffAttempt */); + } + + // JobCompletedListener implementations. + + /** + * A job just finished executing. We fetch the + * {@link com.android.server.job.controllers.JobStatus} from the store and depending on + * whether we want to reschedule we readd it to the controllers. + * @param jobStatus Completed job. + * @param needsReschedule Whether the implementing class should reschedule this job. + */ + @Override + public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) { + if (DEBUG) { + Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); + } + if (!stopTrackingJob(jobStatus)) { + if (DEBUG) { + Slog.e(TAG, "Error removing job: could not find job to remove. Was job " + + "removed while executing?"); + } + return; + } + if (needsReschedule) { + JobStatus rescheduled = getRescheduleJobForFailure(jobStatus); + startTrackingJob(rescheduled); + } else if (jobStatus.getJob().isPeriodic()) { + JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus); + startTrackingJob(rescheduledPeriodic); + } + mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); + } + + // StateChangedListener implementations. + + /** + * Off-board work to our handler thread as quickly as possible, b/c this call is probably being + * made on the main thread. + * For now this takes the job and if it's ready to run it will run it. In future we might not + * provide the job, so that the StateChangedListener has to run through its list of jobs to + * see which are ready. This will further decouple the controllers from the execution logic. + */ + @Override + public void onControllerStateChanged() { + // Post a message to to run through the list of jobs and start/stop any that are eligible. + mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget(); + } + + @Override + public void onRunJobNow(JobStatus jobStatus) { + mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget(); + } + + /** + * Disk I/O is finished, take the list of jobs we read from disk and add them to our + * {@link JobStore}. + * This is run on the {@link com.android.server.IoThread} instance, which is a separate thread, + * and is called once at boot. + */ + @Override + public void onJobMapReadFinished(List<JobStatus> jobs) { + synchronized (mJobs) { + for (JobStatus js : jobs) { + if (mJobs.containsJobIdForUid(js.getJobId(), js.getUid())) { + // An app with BOOT_COMPLETED *might* have decided to reschedule their job, in + // the same amount of time it took us to read it from disk. If this is the case + // we leave it be. + continue; + } + startTrackingJob(js); + } + } + } + + private class JobHandler extends Handler { + + public JobHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MSG_JOB_EXPIRED: + synchronized (mJobs) { + JobStatus runNow = (JobStatus) message.obj; + if (!mPendingJobs.contains(runNow)) { + mPendingJobs.add(runNow); + } + } + queueReadyJobsForExecutionH(); + break; + case MSG_CHECK_JOB: + // Check the list of jobs and run some of them if we feel inclined. + maybeQueueReadyJobsForExecutionH(); + break; + } + maybeRunPendingJobsH(); + // Don't remove JOB_EXPIRED in case one came along while processing the queue. + removeMessages(MSG_CHECK_JOB); + } + + /** + * Run through list of jobs and execute all possible - at least one is expired so we do + * as many as we can. + */ + private void queueReadyJobsForExecutionH() { + synchronized (mJobs) { + for (JobStatus job : mJobs.getJobs()) { + if (isReadyToBeExecutedLocked(job)) { + mPendingJobs.add(job); + } else if (isReadyToBeCancelledLocked(job)) { + stopJobOnServiceContextLocked(job); + } + } + } + } + + /** + * The state of at least one job has changed. Here is where we could enforce various + * policies on when we want to execute jobs. + * Right now the policy is such: + * If >1 of the ready jobs is idle mode we send all of them off + * if more than 2 network connectivity jobs are ready we send them all off. + * If more than 4 jobs total are ready we send them all off. + * TODO: It would be nice to consolidate these sort of high-level policies somewhere. + */ + private void maybeQueueReadyJobsForExecutionH() { + synchronized (mJobs) { + int idleCount = 0; + int backoffCount = 0; + int connectivityCount = 0; + List<JobStatus> runnableJobs = new ArrayList<JobStatus>(); + for (JobStatus job : mJobs.getJobs()) { + if (isReadyToBeExecutedLocked(job)) { + if (job.getNumFailures() > 0) { + backoffCount++; + } + if (job.hasIdleConstraint()) { + idleCount++; + } + if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) { + connectivityCount++; + } + runnableJobs.add(job); + } else if (isReadyToBeCancelledLocked(job)) { + stopJobOnServiceContextLocked(job); + } + } + if (backoffCount > 0 || idleCount >= MIN_IDLE_COUNT || + connectivityCount >= MIN_CONNECTIVITY_COUNT || + runnableJobs.size() >= MIN_READY_JOBS_COUNT) { + for (JobStatus job : runnableJobs) { + mPendingJobs.add(job); + } + } + } + } + + /** + * Criteria for moving a job into the pending queue: + * - It's ready. + * - It's not pending. + * - It's not already running on a JSC. + */ + private boolean isReadyToBeExecutedLocked(JobStatus job) { + return job.isReady() && !mPendingJobs.contains(job) && !isCurrentlyActiveLocked(job); + } + + /** + * Criteria for cancelling an active job: + * - It's not ready + * - It's running on a JSC. + */ + private boolean isReadyToBeCancelledLocked(JobStatus job) { + return !job.isReady() && isCurrentlyActiveLocked(job); + } + + /** + * Reconcile jobs in the pending queue against available execution contexts. + * A controller can force a job into the pending queue even if it's already running, but + * here is where we decide whether to actually execute it. + */ + private void maybeRunPendingJobsH() { + synchronized (mJobs) { + Iterator<JobStatus> it = mPendingJobs.iterator(); + while (it.hasNext()) { + JobStatus nextPending = it.next(); + JobServiceContext availableContext = null; + for (JobServiceContext jsc : mActiveServices) { + final JobStatus running = jsc.getRunningJob(); + if (running != null && running.matches(nextPending.getUid(), + nextPending.getJobId())) { + // Already running this tId for this uId, skip. + availableContext = null; + break; + } + if (jsc.isAvailable()) { + availableContext = jsc; + } + } + if (availableContext != null) { + if (!availableContext.executeRunnableJob(nextPending)) { + if (DEBUG) { + Slog.d(TAG, "Error executing " + nextPending); + } + mJobs.remove(nextPending); + } + it.remove(); + } + } + } + } + } + + /** + * Binder stub trampoline implementation + */ + final class JobSchedulerStub extends IJobScheduler.Stub { + /** Cache determination of whether a given app can persist jobs + * key is uid of the calling app; value is undetermined/true/false + */ + private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); + + // Enforce that only the app itself (or shared uid participant) can schedule a + // job that runs one of the app's services, as well as verifying that the + // named service properly requires the BIND_JOB_SERVICE permission + private void enforceValidJobRequest(int uid, JobInfo job) { + final PackageManager pm = getContext().getPackageManager(); + final ComponentName service = job.getService(); + try { + ServiceInfo si = pm.getServiceInfo(service, 0); + if (si.applicationInfo.uid != uid) { + throw new IllegalArgumentException("uid " + uid + + " cannot schedule job in " + service.getPackageName()); + } + if (!JobService.PERMISSION_BIND.equals(si.permission)) { + throw new IllegalArgumentException("Scheduled service " + service + + " does not require android.permission.BIND_JOB_SERVICE permission"); + } + } catch (NameNotFoundException e) { + throw new IllegalArgumentException("No such service: " + service); + } + } + + private boolean canPersistJobs(int pid, int uid) { + // If we get this far we're good to go; all we need to do now is check + // whether the app is allowed to persist its scheduled work. + final boolean canPersist; + synchronized (mPersistCache) { + Boolean cached = mPersistCache.get(uid); + if (cached != null) { + canPersist = cached.booleanValue(); + } else { + // Persisting jobs is tantamount to running at boot, so we permit + // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED + // permission + int result = getContext().checkPermission( + android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); + canPersist = (result == PackageManager.PERMISSION_GRANTED); + mPersistCache.put(uid, canPersist); + } + } + return canPersist; + } + + // IJobScheduler implementation + @Override + public int schedule(JobInfo job) throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "Scheduling job: " + job); + } + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + + enforceValidJobRequest(uid, job); + final boolean canPersist = canPersistJobs(pid, uid); + + long ident = Binder.clearCallingIdentity(); + try { + return JobSchedulerService.this.schedule(job, uid, canPersist); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public List<JobInfo> getAllPendingJobs() throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + return JobSchedulerService.this.getPendingJobs(uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void cancelAll() throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + JobSchedulerService.this.cancelJobsForUid(uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void cancel(int jobId) throws RemoteException { + final int uid = Binder.getCallingUid(); + + long ident = Binder.clearCallingIdentity(); + try { + JobSchedulerService.this.cancelJob(uid, jobId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** + * "dumpsys" infrastructure + */ + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + + long identityToken = Binder.clearCallingIdentity(); + try { + JobSchedulerService.this.dumpInternal(pw); + } finally { + Binder.restoreCallingIdentity(identityToken); + } + } + }; + + void dumpInternal(PrintWriter pw) { + synchronized (mJobs) { + pw.println("Registered jobs:"); + if (mJobs.size() > 0) { + for (JobStatus job : mJobs.getJobs()) { + job.dump(pw, " "); + } + } else { + pw.println(); + pw.println("No jobs scheduled."); + } + for (StateController controller : mControllers) { + pw.println(); + controller.dumpControllerState(pw); + } + pw.println(); + pw.println("Pending"); + for (JobStatus jobStatus : mPendingJobs) { + pw.println(jobStatus.hashCode()); + } + pw.println(); + pw.println("Active jobs:"); + for (JobServiceContext jsc : mActiveServices) { + if (jsc.isAvailable()) { + continue; + } else { + pw.println(jsc.getRunningJob().hashCode() + " for: " + + (SystemClock.elapsedRealtime() + - jsc.getExecutionStartTimeElapsed())/1000 + "s " + + "timeout: " + jsc.getTimeoutElapsed()); + } + } + } + pw.println(); + } +} diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index a21de88..534faba3 100644 --- a/services/core/java/com/android/server/task/TaskServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -14,12 +14,12 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; import android.app.ActivityManager; -import android.app.task.ITaskCallback; -import android.app.task.ITaskService; -import android.app.task.TaskParams; +import android.app.job.JobParameters; +import android.app.job.IJobCallback; +import android.app.job.IJobService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -39,32 +39,32 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; import java.util.concurrent.atomic.AtomicBoolean; /** - * Handles client binding and lifecycle of a task. A task will only execute one at a time on an + * Handles client binding and lifecycle of a job. A job will only execute one at a time on an * instance of this class. */ -public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection { +public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection { private static final boolean DEBUG = true; - private static final String TAG = "TaskServiceContext"; - /** Define the maximum # of tasks allowed to run on a service at once. */ - private static final int defaultMaxActiveTasksPerService = + private static final String TAG = "JobServiceContext"; + /** Define the maximum # of jobs allowed to run on a service at once. */ + private static final int defaultMaxActiveJobsPerService = ActivityManager.isLowRamDeviceStatic() ? 1 : 3; - /** Amount of time a task is allowed to execute for before being considered timed-out. */ + /** Amount of time a job is allowed to execute for before being considered timed-out. */ private static final long EXECUTING_TIMESLICE_MILLIS = 60 * 1000; - /** Amount of time the TaskManager will wait for a response from an app for a message. */ + /** Amount of time the JobScheduler will wait for a response from an app for a message. */ private static final long OP_TIMEOUT_MILLIS = 8 * 1000; /** String prefix for all wakelock names. */ - private static final String TM_WAKELOCK_PREFIX = "*task*/"; + private static final String JS_WAKELOCK_PREFIX = "*job*/"; private static final String[] VERB_STRINGS = { "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_PENDING" }; - // States that a task occupies while interacting with the client. + // States that a job occupies while interacting with the client. static final int VERB_BINDING = 0; static final int VERB_STARTING = 1; static final int VERB_EXECUTING = 2; @@ -75,30 +75,30 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon private static final int MSG_TIMEOUT = 0; /** Received a callback from client. */ private static final int MSG_CALLBACK = 1; - /** Run through list and start any ready tasks.*/ + /** Run through list and start any ready jobs.*/ private static final int MSG_SERVICE_BOUND = 2; - /** Cancel a task. */ + /** Cancel a job. */ private static final int MSG_CANCEL = 3; - /** Shutdown the Task. Used when the client crashes and we can't die gracefully.*/ + /** Shutdown the job. Used when the client crashes and we can't die gracefully.*/ private static final int MSG_SHUTDOWN_EXECUTION = 4; private final Handler mCallbackHandler; - /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */ - private final TaskCompletedListener mCompletedListener; + /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */ + private final JobCompletedListener mCompletedListener; /** Used for service binding, etc. */ private final Context mContext; private PowerManager.WakeLock mWakeLock; // Execution state. - private TaskParams mParams; + private JobParameters mParams; @VisibleForTesting int mVerb; private AtomicBoolean mCancelled = new AtomicBoolean(); - /** All the information maintained about the task currently being executed. */ - private TaskStatus mRunningTask; + /** All the information maintained about the job currently being executed. */ + private JobStatus mRunningJob; /** Binder to the client service. */ - ITaskService service; + IJobService service; private final Object mLock = new Object(); /** Whether this context is free. */ @@ -109,45 +109,45 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon /** Track when job will timeout. */ private long mTimeoutElapsed; - TaskServiceContext(TaskManagerService service, Looper looper) { + JobServiceContext(JobSchedulerService service, Looper looper) { this(service.getContext(), service, looper); } @VisibleForTesting - TaskServiceContext(Context context, TaskCompletedListener completedListener, Looper looper) { + JobServiceContext(Context context, JobCompletedListener completedListener, Looper looper) { mContext = context; - mCallbackHandler = new TaskServiceHandler(looper); + mCallbackHandler = new JobServiceHandler(looper); mCompletedListener = completedListener; mAvailable = true; } /** - * Give a task to this context for execution. Callers must first check {@link #isAvailable()} + * Give a job to this context for execution. Callers must first check {@link #isAvailable()} * to make sure this is a valid context. - * @param ts The status of the task that we are going to run. - * @return True if the task is valid and is running. False if the task cannot be executed. + * @param job The status of the job that we are going to run. + * @return True if the job is valid and is running. False if the job cannot be executed. */ - boolean executeRunnableTask(TaskStatus ts) { + boolean executeRunnableJob(JobStatus job) { synchronized (mLock) { if (!mAvailable) { Slog.e(TAG, "Starting new runnable but context is unavailable > Error."); return false; } - mRunningTask = ts; - mParams = new TaskParams(ts.getTaskId(), ts.getExtras(), this); + mRunningJob = job; + mParams = new JobParameters(job.getJobId(), job.getExtras(), this); mExecutionStartTimeElapsed = SystemClock.elapsedRealtime(); mVerb = VERB_BINDING; - final Intent intent = new Intent().setComponent(ts.getServiceComponent()); + final Intent intent = new Intent().setComponent(job.getServiceComponent()); boolean binding = mContext.bindServiceAsUser(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND, - new UserHandle(ts.getUserId())); + new UserHandle(job.getUserId())); if (!binding) { if (DEBUG) { - Slog.d(TAG, ts.getServiceComponent().getShortClassName() + " unavailable."); + Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable."); } - mRunningTask = null; + mRunningJob = null; mParams = null; mExecutionStartTimeElapsed = 0L; return false; @@ -157,13 +157,13 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon } } - /** Used externally to query the running task. Will return null if there is no task running. */ - TaskStatus getRunningTask() { - return mRunningTask; + /** Used externally to query the running job. Will return null if there is no job running. */ + JobStatus getRunningJob() { + return mRunningJob; } - /** Called externally when a task that was scheduled for execution should be cancelled. */ - void cancelExecutingTask() { + /** Called externally when a job that was scheduled for execution should be cancelled. */ + void cancelExecutingJob() { mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget(); } @@ -185,29 +185,29 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon } @Override - public void taskFinished(int taskId, boolean reschedule) { + public void jobFinished(int jobId, boolean reschedule) { if (!verifyCallingUid()) { return; } - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0) + mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0) .sendToTarget(); } @Override - public void acknowledgeStopMessage(int taskId, boolean reschedule) { + public void acknowledgeStopMessage(int jobId, boolean reschedule) { if (!verifyCallingUid()) { return; } - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0) + mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0) .sendToTarget(); } @Override - public void acknowledgeStartMessage(int taskId, boolean ongoing) { + public void acknowledgeStartMessage(int jobId, boolean ongoing) { if (!verifyCallingUid()) { return; } - mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, ongoing ? 1 : 0).sendToTarget(); + mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget(); } /** @@ -219,25 +219,25 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon */ @Override public void onServiceConnected(ComponentName name, IBinder service) { - if (!name.equals(mRunningTask.getServiceComponent())) { + if (!name.equals(mRunningJob.getServiceComponent())) { mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); return; } - this.service = ITaskService.Stub.asInterface(service); + this.service = IJobService.Stub.asInterface(service); // Remove all timeouts. mCallbackHandler.removeMessages(MSG_TIMEOUT); final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - TM_WAKELOCK_PREFIX + mRunningTask.getServiceComponent().getPackageName()); - mWakeLock.setWorkSource(new WorkSource(mRunningTask.getUid())); + JS_WAKELOCK_PREFIX + mRunningJob.getServiceComponent().getPackageName()); + mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid())); mWakeLock.setReferenceCounted(false); mWakeLock.acquire(); mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget(); } /** - * If the client service crashes we reschedule this task and clean up. + * If the client service crashes we reschedule this job and clean up. * @param name The concrete component name of the service whose */ @Override @@ -251,7 +251,7 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon * @return True if the binder calling is coming from the client we expect. */ private boolean verifyCallingUid() { - if (mRunningTask == null || Binder.getCallingUid() != mRunningTask.getUid()) { + if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) { if (DEBUG) { Slog.d(TAG, "Stale callback received, ignoring."); } @@ -261,12 +261,12 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon } /** - * Handles the lifecycle of the TaskService binding/callbacks, etc. The convention within this + * Handles the lifecycle of the JobService binding/callbacks, etc. The convention within this * class is to append 'H' to each function name that can only be called on this handler. This * isn't strictly necessary because all of these functions are private, but helps clarity. */ - private class TaskServiceHandler extends Handler { - TaskServiceHandler(Looper looper) { + private class JobServiceHandler extends Handler { + JobServiceHandler(Looper looper) { super(looper); } @@ -278,7 +278,7 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon break; case MSG_CALLBACK: if (DEBUG) { - Slog.d(TAG, "MSG_CALLBACK of : " + mRunningTask + " v:" + + Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob + " v:" + VERB_STRINGS[mVerb]); } removeMessages(MSG_TIMEOUT); @@ -292,7 +292,7 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon handleFinishedH(reschedule); } else { if (DEBUG) { - Slog.d(TAG, "Unrecognised callback: " + mRunningTask); + Slog.d(TAG, "Unrecognised callback: " + mRunningJob); } } break; @@ -303,42 +303,42 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon handleOpTimeoutH(); break; case MSG_SHUTDOWN_EXECUTION: - closeAndCleanupTaskH(true /* needsReschedule */); + closeAndCleanupJobH(true /* needsReschedule */); break; default: Log.e(TAG, "Unrecognised message: " + message); } } - /** Start the task on the service. */ + /** Start the job on the service. */ private void handleServiceBoundH() { if (mVerb != VERB_BINDING) { - Slog.e(TAG, "Sending onStartTask for a task that isn't pending. " + Slog.e(TAG, "Sending onStartJob for a job that isn't pending. " + VERB_STRINGS[mVerb]); - closeAndCleanupTaskH(false /* reschedule */); + closeAndCleanupJobH(false /* reschedule */); return; } if (mCancelled.get()) { if (DEBUG) { - Slog.d(TAG, "Task cancelled while waiting for bind to complete. " - + mRunningTask); + Slog.d(TAG, "Job cancelled while waiting for bind to complete. " + + mRunningJob); } - closeAndCleanupTaskH(true /* reschedule */); + closeAndCleanupJobH(true /* reschedule */); return; } try { mVerb = VERB_STARTING; scheduleOpTimeOut(); - service.startTask(mParams); + service.startJob(mParams); } catch (RemoteException e) { Log.e(TAG, "Error sending onStart message to '" + - mRunningTask.getServiceComponent().getShortClassName() + "' ", e); + mRunningJob.getServiceComponent().getShortClassName() + "' ", e); } } /** * State behaviours. - * VERB_STARTING -> Successful start, change task to VERB_EXECUTING and post timeout. + * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout. * _PENDING -> Error * _EXECUTING -> Error * _STOPPING -> Error @@ -348,7 +348,7 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon case VERB_STARTING: mVerb = VERB_EXECUTING; if (!workOngoing) { - // Task is finished already so fast-forward to handleFinished. + // Job is finished already so fast-forward to handleFinished. handleFinishedH(false); return; } @@ -360,14 +360,14 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon scheduleOpTimeOut(); break; default: - Log.e(TAG, "Handling started task but task wasn't starting! Was " + Log.e(TAG, "Handling started job but job wasn't starting! Was " + VERB_STRINGS[mVerb] + "."); return; } } /** - * VERB_EXECUTING -> Client called taskFinished(), clean up and notify done. + * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done. * _STOPPING -> Successful finish, clean up and notify done. * _STARTING -> Error * _PENDING -> Error @@ -376,20 +376,20 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon switch (mVerb) { case VERB_EXECUTING: case VERB_STOPPING: - closeAndCleanupTaskH(reschedule); + closeAndCleanupJobH(reschedule); break; default: - Slog.e(TAG, "Got an execution complete message for a task that wasn't being" + + Slog.e(TAG, "Got an execution complete message for a job that wasn't being" + "executed. Was " + VERB_STRINGS[mVerb] + "."); } } /** - * A task can be in various states when a cancel request comes in: + * A job can be in various states when a cancel request comes in: * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)} * _STARTING -> Mark as cancelled and wait for - * {@link TaskServiceContext#acknowledgeStartMessage(int, boolean)} + * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)} * _EXECUTING -> call {@link #sendStopMessageH}}. * _ENDING -> No point in doing anything here, so we ignore. */ @@ -406,48 +406,48 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon // Nada. break; default: - Slog.e(TAG, "Cancelling a task without a valid verb: " + mVerb); + Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb); break; } } /** Process MSG_TIMEOUT here. */ private void handleOpTimeoutH() { - if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) { + if (Log.isLoggable(JobSchedulerService.TAG, Log.DEBUG)) { Log.d(TAG, "MSG_TIMEOUT of " + - mRunningTask.getServiceComponent().getShortClassName() + " : " - + mParams.getTaskId()); + mRunningJob.getServiceComponent().getShortClassName() + " : " + + mParams.getJobId()); } - final int taskId = mParams.getTaskId(); + final int jobId = mParams.getJobId(); switch (mVerb) { case VERB_STARTING: // Client unresponsive - wedged or failed to respond in time. We don't really - // know what happened so let's log it and notify the TaskManager + // know what happened so let's log it and notify the JobScheduler // FINISHED/NO-RETRY. - Log.e(TAG, "No response from client for onStartTask '" + - mRunningTask.getServiceComponent().getShortClassName() + "' tId: " - + taskId); - closeAndCleanupTaskH(false /* needsReschedule */); + Log.e(TAG, "No response from client for onStartJob '" + + mRunningJob.getServiceComponent().getShortClassName() + "' tId: " + + jobId); + closeAndCleanupJobH(false /* needsReschedule */); break; case VERB_STOPPING: - // At least we got somewhere, so fail but ask the TaskManager to reschedule. - Log.e(TAG, "No response from client for onStopTask, '" + - mRunningTask.getServiceComponent().getShortClassName() + "' tId: " - + taskId); - closeAndCleanupTaskH(true /* needsReschedule */); + // At least we got somewhere, so fail but ask the JobScheduler to reschedule. + Log.e(TAG, "No response from client for onStopJob, '" + + mRunningJob.getServiceComponent().getShortClassName() + "' tId: " + + jobId); + closeAndCleanupJobH(true /* needsReschedule */); break; case VERB_EXECUTING: // Not an error - client ran out of time. - Log.i(TAG, "Client timed out while executing (no taskFinished received)." + + Log.i(TAG, "Client timed out while executing (no jobFinished received)." + " sending onStop. " + - mRunningTask.getServiceComponent().getShortClassName() + "' tId: " - + taskId); + mRunningJob.getServiceComponent().getShortClassName() + "' tId: " + + jobId); sendStopMessageH(); break; default: - Log.e(TAG, "Handling timeout for an unknown active task state: " - + mRunningTask); + Log.e(TAG, "Handling timeout for an unknown active job state: " + + mRunningJob); return; } } @@ -459,35 +459,34 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon private void sendStopMessageH() { mCallbackHandler.removeMessages(MSG_TIMEOUT); if (mVerb != VERB_EXECUTING) { - Log.e(TAG, "Sending onStopTask for a task that isn't started. " + mRunningTask); - closeAndCleanupTaskH(false /* reschedule */); + Log.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob); + closeAndCleanupJobH(false /* reschedule */); return; } try { mVerb = VERB_STOPPING; scheduleOpTimeOut(); - service.stopTask(mParams); + service.stopJob(mParams); } catch (RemoteException e) { - Log.e(TAG, "Error sending onStopTask to client.", e); - closeAndCleanupTaskH(false /* reschedule */); + Log.e(TAG, "Error sending onStopJob to client.", e); + closeAndCleanupJobH(false /* reschedule */); } } /** - * The provided task has finished, either by calling - * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)} + * The provided job has finished, either by calling + * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)} * or from acknowledging the stop message we sent. Either way, we're done tracking it and * we want to clean up internally. */ - private void closeAndCleanupTaskH(boolean reschedule) { + private void closeAndCleanupJobH(boolean reschedule) { removeMessages(MSG_TIMEOUT); + mCompletedListener.onJobCompleted(mRunningJob, reschedule); synchronized (mLock) { mWakeLock.release(); - mContext.unbindService(TaskServiceContext.this); - mCompletedListener.onTaskCompleted(mRunningTask, reschedule); - + mContext.unbindService(JobServiceContext.this); mWakeLock = null; - mRunningTask = null; + mRunningJob = null; mParams = null; mVerb = -1; mCancelled.set(false); @@ -508,8 +507,8 @@ public class TaskServiceContext extends ITaskCallback.Stub implements ServiceCon EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS; if (DEBUG) { Slog.d(TAG, "Scheduling time out for '" + - mRunningTask.getServiceComponent().getShortClassName() + "' tId: " + - mParams.getTaskId() + ", in " + (timeoutMillis / 1000) + " s"); + mRunningJob.getServiceComponent().getShortClassName() + "' tId: " + + mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s"); } Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT); mCallbackHandler.sendMessageDelayed(m, timeoutMillis); diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/job/JobStore.java index 9e095e7..4ac26c1 100644 --- a/services/core/java/com/android/server/task/TaskStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -14,10 +14,10 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; import android.content.ComponentName; -import android.app.task.Task; +import android.app.job.JobInfo; import android.content.Context; import android.os.Environment; import android.os.Handler; @@ -33,7 +33,7 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FastXmlSerializer; import com.android.server.IoThread; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; import java.io.ByteArrayOutputStream; import java.io.File; @@ -50,94 +50,94 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; /** - * Maintain a list of classes, and accessor methods/logic for these tasks. + * Maintain a list of classes, and accessor methods/logic for these jobs. * This class offers the following functionality: - * - When a task is added, it will determine if the task requirements have changed (update) and + * - When a job is added, it will determine if the job requirements have changed (update) and * whether the controllers need to be updated. - * - Persists Tasks, figures out when to to rewrite the Task to disk. - * - Handles rescheduling of tasks. - * - When a periodic task is executed and must be re-added. - * - When a task fails and the client requests that it be retried with backoff. + * - Persists JobInfos, figures out when to to rewrite the JobInfo to disk. + * - Handles rescheduling of jobs. + * - When a periodic job is executed and must be re-added. + * - When a job fails and the client requests that it be retried with backoff. * - This class <strong>is not</strong> thread-safe. * * Note on locking: * All callers to this class must <strong>lock on the class object they are calling</strong>. - * This is important b/c {@link com.android.server.task.TaskStore.WriteTasksMapToDiskRunnable} - * and {@link com.android.server.task.TaskStore.ReadTaskMapFromDiskRunnable} lock on that + * This is important b/c {@link com.android.server.job.JobStore.WriteJobsMapToDiskRunnable} + * and {@link com.android.server.job.JobStore.ReadJobMapFromDiskRunnable} lock on that * object. */ -public class TaskStore { - private static final String TAG = "TaskManagerStore"; - private static final boolean DEBUG = TaskManagerService.DEBUG; +public class JobStore { + private static final String TAG = "JobStore"; + private static final boolean DEBUG = JobSchedulerService.DEBUG; /** Threshold to adjust how often we want to write to the db. */ private static final int MAX_OPS_BEFORE_WRITE = 1; - final ArraySet<TaskStatus> mTasksSet; + final ArraySet<JobStatus> mJobSet; final Context mContext; private int mDirtyOperations; private static final Object sSingletonLock = new Object(); - private final AtomicFile mTasksFile; + private final AtomicFile mJobsFile; /** Handler backed by IoThread for writing to disk. */ private final Handler mIoHandler = IoThread.getHandler(); - private static TaskStore sSingleton; + private static JobStore sSingleton; - /** Used by the {@Link TaskManagerService} to instantiate the TaskStore. */ - static TaskStore initAndGet(TaskManagerService taskManagerService) { + /** Used by the {@link JobSchedulerService} to instantiate the JobStore. */ + static JobStore initAndGet(JobSchedulerService jobManagerService) { synchronized (sSingletonLock) { if (sSingleton == null) { - sSingleton = new TaskStore(taskManagerService.getContext(), - Environment.getDataDirectory(), taskManagerService); + sSingleton = new JobStore(jobManagerService.getContext(), + Environment.getDataDirectory(), jobManagerService); } return sSingleton; } } @VisibleForTesting - public static TaskStore initAndGetForTesting(Context context, File dataDir, - TaskMapReadFinishedListener callback) { - return new TaskStore(context, dataDir, callback); + public static JobStore initAndGetForTesting(Context context, File dataDir, + JobMapReadFinishedListener callback) { + return new JobStore(context, dataDir, callback); } - private TaskStore(Context context, File dataDir, TaskMapReadFinishedListener callback) { + private JobStore(Context context, File dataDir, JobMapReadFinishedListener callback) { mContext = context; mDirtyOperations = 0; File systemDir = new File(dataDir, "system"); - File taskDir = new File(systemDir, "task"); - taskDir.mkdirs(); - mTasksFile = new AtomicFile(new File(taskDir, "tasks.xml")); + File jobDir = new File(systemDir, "job"); + jobDir.mkdirs(); + mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml")); - mTasksSet = new ArraySet<TaskStatus>(); + mJobSet = new ArraySet<JobStatus>(); - readTaskMapFromDiskAsync(callback); + readJobMapFromDiskAsync(callback); } /** - * Add a task to the master list, persisting it if necessary. If the TaskStatus already exists, + * Add a job to the master list, persisting it if necessary. If the JobStatus already exists, * it will be replaced. - * @param taskStatus Task to add. - * @return Whether or not an equivalent TaskStatus was replaced by this operation. + * @param jobStatus Job to add. + * @return Whether or not an equivalent JobStatus was replaced by this operation. */ - public boolean add(TaskStatus taskStatus) { - boolean replaced = mTasksSet.remove(taskStatus); - mTasksSet.add(taskStatus); - if (taskStatus.isPersisted()) { + public boolean add(JobStatus jobStatus) { + boolean replaced = mJobSet.remove(jobStatus); + mJobSet.add(jobStatus); + if (jobStatus.isPersisted()) { maybeWriteStatusToDiskAsync(); } if (DEBUG) { - Slog.d(TAG, "Added task status to store: " + taskStatus); + Slog.d(TAG, "Added job status to store: " + jobStatus); } return replaced; } /** - * Whether this taskStatus object already exists in the TaskStore. + * Whether this jobStatus object already exists in the JobStore. */ - public boolean containsTaskIdForUid(int taskId, int uId) { - for (TaskStatus ts : mTasksSet) { - if (ts.getUid() == uId && ts.getTaskId() == taskId) { + public boolean containsJobIdForUid(int jobId, int uId) { + for (JobStatus ts : mJobSet) { + if (ts.getUid() == uId && ts.getJobId() == jobId) { return true; } } @@ -145,18 +145,18 @@ public class TaskStore { } public int size() { - return mTasksSet.size(); + return mJobSet.size(); } /** - * Remove the provided task. Will also delete the task if it was persisted. - * @return Whether or not the task existed to be removed. + * Remove the provided job. Will also delete the job if it was persisted. + * @return Whether or not the job existed to be removed. */ - public boolean remove(TaskStatus taskStatus) { - boolean removed = mTasksSet.remove(taskStatus); + public boolean remove(JobStatus jobStatus) { + boolean removed = mJobSet.remove(jobStatus); if (!removed) { if (DEBUG) { - Slog.d(TAG, "Couldn't remove task: didn't exist: " + taskStatus); + Slog.d(TAG, "Couldn't remove job: didn't exist: " + jobStatus); } return false; } @@ -166,48 +166,48 @@ public class TaskStore { @VisibleForTesting public void clear() { - mTasksSet.clear(); + mJobSet.clear(); maybeWriteStatusToDiskAsync(); } - public List<TaskStatus> getTasksByUser(int userHandle) { - List<TaskStatus> matchingTasks = new ArrayList<TaskStatus>(); - Iterator<TaskStatus> it = mTasksSet.iterator(); + public List<JobStatus> getJobsByUser(int userHandle) { + List<JobStatus> matchingJobs = new ArrayList<JobStatus>(); + Iterator<JobStatus> it = mJobSet.iterator(); while (it.hasNext()) { - TaskStatus ts = it.next(); + JobStatus ts = it.next(); if (UserHandle.getUserId(ts.getUid()) == userHandle) { - matchingTasks.add(ts); + matchingJobs.add(ts); } } - return matchingTasks; + return matchingJobs; } /** * @param uid Uid of the requesting app. - * @return All TaskStatus objects for a given uid from the master list. + * @return All JobStatus objects for a given uid from the master list. */ - public List<TaskStatus> getTasksByUid(int uid) { - List<TaskStatus> matchingTasks = new ArrayList<TaskStatus>(); - Iterator<TaskStatus> it = mTasksSet.iterator(); + public List<JobStatus> getJobsByUid(int uid) { + List<JobStatus> matchingJobs = new ArrayList<JobStatus>(); + Iterator<JobStatus> it = mJobSet.iterator(); while (it.hasNext()) { - TaskStatus ts = it.next(); + JobStatus ts = it.next(); if (ts.getUid() == uid) { - matchingTasks.add(ts); + matchingJobs.add(ts); } } - return matchingTasks; + return matchingJobs; } /** * @param uid Uid of the requesting app. - * @param taskId Task id, specified at schedule-time. - * @return the TaskStatus that matches the provided uId and taskId, or null if none found. + * @param jobId Job id, specified at schedule-time. + * @return the JobStatus that matches the provided uId and jobId, or null if none found. */ - public TaskStatus getTaskByUidAndTaskId(int uid, int taskId) { - Iterator<TaskStatus> it = mTasksSet.iterator(); + public JobStatus getJobByUidAndJobId(int uid, int jobId) { + Iterator<JobStatus> it = mJobSet.iterator(); while (it.hasNext()) { - TaskStatus ts = it.next(); - if (ts.getUid() == uid && ts.getTaskId() == taskId) { + JobStatus ts = it.next(); + if (ts.getUid() == uid && ts.getJobId() == jobId) { return ts; } } @@ -215,15 +215,15 @@ public class TaskStore { } /** - * @return The live array of TaskStatus objects. + * @return The live array of JobStatus objects. */ - public ArraySet<TaskStatus> getTasks() { - return mTasksSet; + public ArraySet<JobStatus> getJobs() { + return mJobSet; } /** Version of the db schema. */ - private static final int TASKS_FILE_VERSION = 0; - /** Tag corresponds to constraints this task needs. */ + private static final int JOBS_FILE_VERSION = 0; + /** Tag corresponds to constraints this job needs. */ private static final String XML_TAG_PARAMS_CONSTRAINTS = "constraints"; /** Tag corresponds to execution parameters. */ private static final String XML_TAG_PERIODIC = "periodic"; @@ -231,7 +231,7 @@ public class TaskStore { private static final String XML_TAG_EXTRAS = "extras"; /** - * Every time the state changes we write all the tasks in one swathe, instead of trying to + * Every time the state changes we write all the jobs in one swath, instead of trying to * track incremental changes. * @return Whether the operation was successful. This will only fail for e.g. if the system is * low on storage. If this happens, we continue as normal @@ -240,38 +240,45 @@ public class TaskStore { mDirtyOperations++; if (mDirtyOperations >= MAX_OPS_BEFORE_WRITE) { if (DEBUG) { - Slog.v(TAG, "Writing tasks to disk."); + Slog.v(TAG, "Writing jobs to disk."); } - mIoHandler.post(new WriteTasksMapToDiskRunnable()); + mIoHandler.post(new WriteJobsMapToDiskRunnable()); } } - private void readTaskMapFromDiskAsync(TaskMapReadFinishedListener callback) { - mIoHandler.post(new ReadTaskMapFromDiskRunnable(callback)); + private void readJobMapFromDiskAsync(JobMapReadFinishedListener callback) { + mIoHandler.post(new ReadJobMapFromDiskRunnable(callback)); } - public void readTaskMapFromDisk(TaskMapReadFinishedListener callback) { - new ReadTaskMapFromDiskRunnable(callback).run(); + public void readJobMapFromDisk(JobMapReadFinishedListener callback) { + new ReadJobMapFromDiskRunnable(callback).run(); } /** - * Runnable that writes {@link #mTasksSet} out to xml. - * NOTE: This Runnable locks on TaskStore.this + * Runnable that writes {@link #mJobSet} out to xml. + * NOTE: This Runnable locks on JobStore.this */ - private class WriteTasksMapToDiskRunnable implements Runnable { + private class WriteJobsMapToDiskRunnable implements Runnable { @Override public void run() { final long startElapsed = SystemClock.elapsedRealtime(); - synchronized (TaskStore.this) { - writeTasksMapImpl(); + List<JobStatus> mStoreCopy = new ArrayList<JobStatus>(); + synchronized (JobStore.this) { + // Copy over the jobs so we can release the lock before writing. + for (JobStatus jobStatus : mJobSet) { + JobStatus copy = new JobStatus(jobStatus.getJob(), jobStatus.getUid(), + jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed()); + mStoreCopy.add(copy); + } } - if (TaskManagerService.DEBUG) { + writeJobsMapImpl(mStoreCopy); + if (JobSchedulerService.DEBUG) { Slog.v(TAG, "Finished writing, took " + (SystemClock.elapsedRealtime() - startElapsed) + "ms"); } } - private void writeTasksMapImpl() { + private void writeJobsMapImpl(List<JobStatus> jobList) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); XmlSerializer out = new FastXmlSerializer(); @@ -279,31 +286,30 @@ public class TaskStore { out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - out.startTag(null, "task-info"); - out.attribute(null, "version", Integer.toString(TASKS_FILE_VERSION)); - for (int i = 0; i < mTasksSet.size(); i++) { - final TaskStatus taskStatus = mTasksSet.valueAt(i); + out.startTag(null, "job-info"); + out.attribute(null, "version", Integer.toString(JOBS_FILE_VERSION)); + for (JobStatus jobStatus : jobList) { if (DEBUG) { - Slog.d(TAG, "Saving task " + taskStatus.getTaskId()); + Slog.d(TAG, "Saving job " + jobStatus.getJobId()); } - out.startTag(null, "task"); - addIdentifierAttributesToTaskTag(out, taskStatus); - writeConstraintsToXml(out, taskStatus); - writeExecutionCriteriaToXml(out, taskStatus); - writeBundleToXml(taskStatus.getExtras(), out); - out.endTag(null, "task"); + out.startTag(null, "job"); + addIdentifierAttributesToJobTag(out, jobStatus); + writeConstraintsToXml(out, jobStatus); + writeExecutionCriteriaToXml(out, jobStatus); + writeBundleToXml(jobStatus.getExtras(), out); + out.endTag(null, "job"); } - out.endTag(null, "task-info"); + out.endTag(null, "job-info"); out.endDocument(); // Write out to disk in one fell sweep. - FileOutputStream fos = mTasksFile.startWrite(); + FileOutputStream fos = mJobsFile.startWrite(); fos.write(baos.toByteArray()); - mTasksFile.finishWrite(fos); + mJobsFile.finishWrite(fos); mDirtyOperations = 0; } catch (IOException e) { if (DEBUG) { - Slog.v(TAG, "Error writing out task data.", e); + Slog.v(TAG, "Error writing out job data.", e); } } catch (XmlPullParserException e) { if (DEBUG) { @@ -312,13 +318,13 @@ public class TaskStore { } } - /** Write out a tag with data comprising the required fields of this task and its client. */ - private void addIdentifierAttributesToTaskTag(XmlSerializer out, TaskStatus taskStatus) + /** Write out a tag with data comprising the required fields of this job and its client. */ + private void addIdentifierAttributesToJobTag(XmlSerializer out, JobStatus jobStatus) throws IOException { - out.attribute(null, "taskid", Integer.toString(taskStatus.getTaskId())); - out.attribute(null, "package", taskStatus.getServiceComponent().getPackageName()); - out.attribute(null, "class", taskStatus.getServiceComponent().getClassName()); - out.attribute(null, "uid", Integer.toString(taskStatus.getUid())); + out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId())); + out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName()); + out.attribute(null, "class", jobStatus.getServiceComponent().getClassName()); + out.attribute(null, "uid", Integer.toString(jobStatus.getUid())); } private void writeBundleToXml(PersistableBundle extras, XmlSerializer out) @@ -328,57 +334,57 @@ public class TaskStore { out.endTag(null, XML_TAG_EXTRAS); } /** - * Write out a tag with data identifying this tasks constraints. If the constraint isn't here + * Write out a tag with data identifying this job's constraints. If the constraint isn't here * it doesn't apply. */ - private void writeConstraintsToXml(XmlSerializer out, TaskStatus taskStatus) throws IOException { + private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException { out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS); - if (taskStatus.hasUnmeteredConstraint()) { + if (jobStatus.hasUnmeteredConstraint()) { out.attribute(null, "unmetered", Boolean.toString(true)); } - if (taskStatus.hasConnectivityConstraint()) { + if (jobStatus.hasConnectivityConstraint()) { out.attribute(null, "connectivity", Boolean.toString(true)); } - if (taskStatus.hasIdleConstraint()) { + if (jobStatus.hasIdleConstraint()) { out.attribute(null, "idle", Boolean.toString(true)); } - if (taskStatus.hasChargingConstraint()) { + if (jobStatus.hasChargingConstraint()) { out.attribute(null, "charging", Boolean.toString(true)); } out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS); } - private void writeExecutionCriteriaToXml(XmlSerializer out, TaskStatus taskStatus) + private void writeExecutionCriteriaToXml(XmlSerializer out, JobStatus jobStatus) throws IOException { - final Task task = taskStatus.getTask(); - if (taskStatus.getTask().isPeriodic()) { + final JobInfo job = jobStatus.getJob(); + if (jobStatus.getJob().isPeriodic()) { out.startTag(null, XML_TAG_PERIODIC); - out.attribute(null, "period", Long.toString(task.getIntervalMillis())); + out.attribute(null, "period", Long.toString(job.getIntervalMillis())); } else { out.startTag(null, XML_TAG_ONEOFF); } - if (taskStatus.hasDeadlineConstraint()) { + if (jobStatus.hasDeadlineConstraint()) { // Wall clock deadline. final long deadlineWallclock = System.currentTimeMillis() + - (taskStatus.getLatestRunTimeElapsed() - SystemClock.elapsedRealtime()); + (jobStatus.getLatestRunTimeElapsed() - SystemClock.elapsedRealtime()); out.attribute(null, "deadline", Long.toString(deadlineWallclock)); } - if (taskStatus.hasTimingDelayConstraint()) { + if (jobStatus.hasTimingDelayConstraint()) { final long delayWallclock = System.currentTimeMillis() + - (taskStatus.getEarliestRunTime() - SystemClock.elapsedRealtime()); + (jobStatus.getEarliestRunTime() - SystemClock.elapsedRealtime()); out.attribute(null, "delay", Long.toString(delayWallclock)); } // Only write out back-off policy if it differs from the default. - // This also helps the case where the task is idle -> these aren't allowed to specify + // This also helps the case where the job is idle -> these aren't allowed to specify // back-off. - if (taskStatus.getTask().getInitialBackoffMillis() != Task.DEFAULT_INITIAL_BACKOFF_MILLIS - || taskStatus.getTask().getBackoffPolicy() != Task.DEFAULT_BACKOFF_POLICY) { - out.attribute(null, "backoff-policy", Integer.toString(task.getBackoffPolicy())); - out.attribute(null, "initial-backoff", Long.toString(task.getInitialBackoffMillis())); + if (jobStatus.getJob().getInitialBackoffMillis() != JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS + || jobStatus.getJob().getBackoffPolicy() != JobInfo.DEFAULT_BACKOFF_POLICY) { + out.attribute(null, "backoff-policy", Integer.toString(job.getBackoffPolicy())); + out.attribute(null, "initial-backoff", Long.toString(job.getInitialBackoffMillis())); } - if (task.isPeriodic()) { + if (job.isPeriodic()) { out.endTag(null, XML_TAG_PERIODIC); } else { out.endTag(null, XML_TAG_ONEOFF); @@ -387,43 +393,43 @@ public class TaskStore { } /** - * Runnable that reads list of persisted task from xml. - * NOTE: This Runnable locks on TaskStore.this + * Runnable that reads list of persisted job from xml. + * NOTE: This Runnable locks on JobStore.this */ - private class ReadTaskMapFromDiskRunnable implements Runnable { - private TaskMapReadFinishedListener mCallback; - public ReadTaskMapFromDiskRunnable(TaskMapReadFinishedListener callback) { + private class ReadJobMapFromDiskRunnable implements Runnable { + private JobMapReadFinishedListener mCallback; + public ReadJobMapFromDiskRunnable(JobMapReadFinishedListener callback) { mCallback = callback; } @Override public void run() { try { - List<TaskStatus> tasks; - FileInputStream fis = mTasksFile.openRead(); - synchronized (TaskStore.this) { - tasks = readTaskMapImpl(fis); + List<JobStatus> jobs; + FileInputStream fis = mJobsFile.openRead(); + synchronized (JobStore.this) { + jobs = readJobMapImpl(fis); } fis.close(); - if (tasks != null) { - mCallback.onTaskMapReadFinished(tasks); + if (jobs != null) { + mCallback.onJobMapReadFinished(jobs); } } catch (FileNotFoundException e) { - if (TaskManagerService.DEBUG) { - Slog.d(TAG, "Could not find tasks file, probably there was nothing to load."); + if (JobSchedulerService.DEBUG) { + Slog.d(TAG, "Could not find jobs file, probably there was nothing to load."); } } catch (XmlPullParserException e) { - if (TaskManagerService.DEBUG) { + if (JobSchedulerService.DEBUG) { Slog.d(TAG, "Error parsing xml.", e); } } catch (IOException e) { - if (TaskManagerService.DEBUG) { + if (JobSchedulerService.DEBUG) { Slog.d(TAG, "Error parsing xml.", e); } } } - private List<TaskStatus> readTaskMapImpl(FileInputStream fis) throws XmlPullParserException, IOException { + private List<JobStatus> readJobMapImpl(FileInputStream fis) throws XmlPullParserException, IOException { XmlPullParser parser = Xml.newPullParser(); parser.setInput(fis, null); @@ -435,66 +441,66 @@ public class TaskStore { } if (eventType == XmlPullParser.END_DOCUMENT) { if (DEBUG) { - Slog.d(TAG, "No persisted tasks."); + Slog.d(TAG, "No persisted jobs."); } return null; } String tagName = parser.getName(); - if ("task-info".equals(tagName)) { - final List<TaskStatus> tasks = new ArrayList<TaskStatus>(); + if ("job-info".equals(tagName)) { + final List<JobStatus> jobs = new ArrayList<JobStatus>(); // Read in version info. try { int version = Integer.valueOf(parser.getAttributeValue(null, "version")); - if (version != TASKS_FILE_VERSION) { - Slog.d(TAG, "Invalid version number, aborting tasks file read."); + if (version != JOBS_FILE_VERSION) { + Slog.d(TAG, "Invalid version number, aborting jobs file read."); return null; } } catch (NumberFormatException e) { - Slog.e(TAG, "Invalid version number, aborting tasks file read."); + Slog.e(TAG, "Invalid version number, aborting jobs file read."); return null; } eventType = parser.next(); do { - // Read each <task/> + // Read each <job/> if (eventType == XmlPullParser.START_TAG) { tagName = parser.getName(); - // Start reading task. - if ("task".equals(tagName)) { - TaskStatus persistedTask = restoreTaskFromXml(parser); - if (persistedTask != null) { + // Start reading job. + if ("job".equals(tagName)) { + JobStatus persistedJob = restoreJobFromXml(parser); + if (persistedJob != null) { if (DEBUG) { - Slog.d(TAG, "Read out " + persistedTask); + Slog.d(TAG, "Read out " + persistedJob); } - tasks.add(persistedTask); + jobs.add(persistedJob); } else { - Slog.d(TAG, "Error reading task from file."); + Slog.d(TAG, "Error reading job from file."); } } } eventType = parser.next(); } while (eventType != XmlPullParser.END_DOCUMENT); - return tasks; + return jobs; } return null; } /** - * @param parser Xml parser at the beginning of a "<task/>" tag. The next "parser.next()" call - * will take the parser into the body of the task tag. - * @return Newly instantiated task holding all the information we just read out of the xml tag. + * @param parser Xml parser at the beginning of a "<job/>" tag. The next "parser.next()" call + * will take the parser into the body of the job tag. + * @return Newly instantiated job holding all the information we just read out of the xml tag. */ - private TaskStatus restoreTaskFromXml(XmlPullParser parser) throws XmlPullParserException, + private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserException, IOException { - Task.Builder taskBuilder; + JobInfo.Builder jobBuilder; int uid; - // Read out task identifier attributes. + // Read out job identifier attributes. try { - taskBuilder = buildBuilderFromXml(parser); + jobBuilder = buildBuilderFromXml(parser); uid = Integer.valueOf(parser.getAttributeValue(null, "uid")); } catch (NumberFormatException e) { - Slog.e(TAG, "Error parsing task's required fields, skipping"); + Slog.e(TAG, "Error parsing job's required fields, skipping"); return null; } @@ -510,7 +516,7 @@ public class TaskStore { return null; } try { - buildConstraintsFromXml(taskBuilder, parser); + buildConstraintsFromXml(jobBuilder, parser); } catch (NumberFormatException e) { Slog.d(TAG, "Error reading constraints, skipping."); return null; @@ -538,22 +544,22 @@ public class TaskStore { if (XML_TAG_PERIODIC.equals(parser.getName())) { try { String val = parser.getAttributeValue(null, "period"); - taskBuilder.setPeriodic(Long.valueOf(val)); + jobBuilder.setPeriodic(Long.valueOf(val)); } catch (NumberFormatException e) { Slog.d(TAG, "Error reading periodic execution criteria, skipping."); return null; } } else if (XML_TAG_ONEOFF.equals(parser.getName())) { try { - if (runtimes.first != TaskStatus.NO_EARLIEST_RUNTIME) { - taskBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime()); + if (runtimes.first != JobStatus.NO_EARLIEST_RUNTIME) { + jobBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime()); } - if (runtimes.second != TaskStatus.NO_LATEST_RUNTIME) { - taskBuilder.setOverrideDeadline( + if (runtimes.second != JobStatus.NO_LATEST_RUNTIME) { + jobBuilder.setOverrideDeadline( runtimes.second - SystemClock.elapsedRealtime()); } } catch (NumberFormatException e) { - Slog.d(TAG, "Error reading task execution criteria, skipping."); + Slog.d(TAG, "Error reading job execution criteria, skipping."); return null; } } else { @@ -563,7 +569,7 @@ public class TaskStore { // Expecting a parameters start tag. return null; } - maybeBuildBackoffPolicyFromXml(taskBuilder, parser); + maybeBuildBackoffPolicyFromXml(jobBuilder, parser); parser.nextTag(); // Consume parameters end tag. @@ -579,52 +585,52 @@ public class TaskStore { } PersistableBundle extras = PersistableBundle.restoreFromXml(parser); - taskBuilder.setExtras(extras); + jobBuilder.setExtras(extras); parser.nextTag(); // Consume </extras> - return new TaskStatus(taskBuilder.build(), uid, runtimes.first, runtimes.second); + return new JobStatus(jobBuilder.build(), uid, runtimes.first, runtimes.second); } - private Task.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException { - // Pull out required fields from <task> attributes. - int taskId = Integer.valueOf(parser.getAttributeValue(null, "taskid")); + private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException { + // Pull out required fields from <job> attributes. + int jobId = Integer.valueOf(parser.getAttributeValue(null, "jobid")); String packageName = parser.getAttributeValue(null, "package"); String className = parser.getAttributeValue(null, "class"); ComponentName cname = new ComponentName(packageName, className); - return new Task.Builder(taskId, cname); + return new JobInfo.Builder(jobId, cname); } - private void buildConstraintsFromXml(Task.Builder taskBuilder, XmlPullParser parser) { + private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { String val = parser.getAttributeValue(null, "unmetered"); if (val != null) { - taskBuilder.setRequiredNetworkCapabilities(Task.NetworkType.UNMETERED); + jobBuilder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED); } val = parser.getAttributeValue(null, "connectivity"); if (val != null) { - taskBuilder.setRequiredNetworkCapabilities(Task.NetworkType.ANY); + jobBuilder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY); } val = parser.getAttributeValue(null, "idle"); if (val != null) { - taskBuilder.setRequiresDeviceIdle(true); + jobBuilder.setRequiresDeviceIdle(true); } val = parser.getAttributeValue(null, "charging"); if (val != null) { - taskBuilder.setRequiresCharging(true); + jobBuilder.setRequiresCharging(true); } } /** * Builds the back-off policy out of the params tag. These attributes may not exist, depending - * on whether the back-off was set when the task was first scheduled. + * on whether the back-off was set when the job was first scheduled. */ - private void maybeBuildBackoffPolicyFromXml(Task.Builder taskBuilder, XmlPullParser parser) { + private void maybeBuildBackoffPolicyFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { String val = parser.getAttributeValue(null, "initial-backoff"); if (val != null) { long initialBackoff = Long.valueOf(val); val = parser.getAttributeValue(null, "backoff-policy"); int backoffPolicy = Integer.valueOf(val); // Will throw NFE which we catch higher up. - taskBuilder.setBackoffCriteria(initialBackoff, backoffPolicy); + jobBuilder.setBackoffCriteria(initialBackoff, backoffPolicy); } } @@ -640,8 +646,8 @@ public class TaskStore { final long nowWallclock = System.currentTimeMillis(); final long nowElapsed = SystemClock.elapsedRealtime(); - long earliestRunTimeElapsed = TaskStatus.NO_EARLIEST_RUNTIME; - long latestRunTimeElapsed = TaskStatus.NO_LATEST_RUNTIME; + long earliestRunTimeElapsed = JobStatus.NO_EARLIEST_RUNTIME; + long latestRunTimeElapsed = JobStatus.NO_LATEST_RUNTIME; String val = parser.getAttributeValue(null, "deadline"); if (val != null) { long latestRuntimeWallclock = Long.valueOf(val); diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/job/StateChangedListener.java index ab5cc7c..90c203a 100644 --- a/services/core/java/com/android/server/task/StateChangedListener.java +++ b/services/core/java/com/android/server/job/StateChangedListener.java @@ -14,26 +14,26 @@ * limitations under the License */ -package com.android.server.task; +package com.android.server.job; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.controllers.JobStatus; /** - * Interface through which a {@link com.android.server.task.controllers.StateController} informs - * the {@link com.android.server.task.TaskManagerService} that there are some tasks potentially + * Interface through which a {@link com.android.server.job.controllers.StateController} informs + * the {@link com.android.server.job.JobSchedulerService} that there are some tasks potentially * ready to be run. */ public interface StateChangedListener { /** - * Called by the controller to notify the TaskManager that it should check on the state of a + * Called by the controller to notify the JobManager that it should check on the state of a * task. */ public void onControllerStateChanged(); /** - * Called by the controller to notify the TaskManager that regardless of the state of the task, + * Called by the controller to notify the JobManager that regardless of the state of the task, * it must be run immediately. - * @param taskStatus The state of the task which is to be run immediately. + * @param jobStatus The state of the task which is to be run immediately. */ - public void onRunTaskNow(TaskStatus taskStatus); + public void onRunJobNow(JobStatus jobStatus); } diff --git a/services/core/java/com/android/server/task/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java index 443527f..4aef2d3 100644 --- a/services/core/java/com/android/server/task/controllers/BatteryController.java +++ b/services/core/java/com/android/server/job/controllers/BatteryController.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import android.app.AlarmManager; import android.app.PendingIntent; @@ -32,11 +32,12 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.BatteryService; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -45,7 +46,7 @@ import java.util.List; * ACTION_BATTERY_OK. */ public class BatteryController extends StateController { - private static final String TAG = "BatteryController"; + private static final String TAG = "JobScheduler.Batt"; private static final Object sCreationLock = new Object(); private static volatile BatteryController sController; @@ -54,10 +55,10 @@ public class BatteryController extends StateController { /** Wait this long after phone is plugged in before doing any work. */ private static final long STABLE_CHARGING_THRESHOLD_MILLIS = 2 * 60 * 1000; // 2 minutes. - private List<TaskStatus> mTrackedTasks = new ArrayList<TaskStatus>(); + private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); private ChargingTracker mChargeTracker; - public static BatteryController get(TaskManagerService taskManagerService) { + public static BatteryController get(JobSchedulerService taskManagerService) { synchronized (sCreationLock) { if (sController == null) { sController = new BatteryController(taskManagerService, @@ -85,18 +86,21 @@ public class BatteryController extends StateController { } @Override - public void maybeStartTrackingTask(TaskStatus taskStatus) { + public void maybeStartTrackingJob(JobStatus taskStatus) { if (taskStatus.hasChargingConstraint()) { + final boolean isOnStablePower = mChargeTracker.isOnStablePower(); synchronized (mTrackedTasks) { mTrackedTasks.add(taskStatus); - taskStatus.chargingConstraintSatisfied.set(mChargeTracker.isOnStablePower()); + taskStatus.chargingConstraintSatisfied.set(isOnStablePower); + } + if (isOnStablePower) { + mStateChangedListener.onControllerStateChanged(); } } - } @Override - public void maybeStopTrackingTask(TaskStatus taskStatus) { + public void maybeStopTrackingJob(JobStatus taskStatus) { if (taskStatus.hasChargingConstraint()) { synchronized (mTrackedTasks) { mTrackedTasks.remove(taskStatus); @@ -106,9 +110,12 @@ public class BatteryController extends StateController { private void maybeReportNewChargingState() { final boolean stablePower = mChargeTracker.isOnStablePower(); + if (DEBUG) { + Slog.d(TAG, "maybeReportNewChargingState: " + stablePower); + } boolean reportChange = false; synchronized (mTrackedTasks) { - for (TaskStatus ts : mTrackedTasks) { + for (JobStatus ts : mTrackedTasks) { boolean previous = ts.chargingConstraintSatisfied.getAndSet(stablePower); if (previous != stablePower) { reportChange = true; @@ -133,8 +140,7 @@ public class BatteryController extends StateController { public ChargingTracker() { mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - Intent intent = new Intent(ACTION_CHARGING_STABLE) - .setComponent(new ComponentName(mContext, this.getClass())); + Intent intent = new Intent(ACTION_CHARGING_STABLE); mStableChargingTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); } @@ -147,6 +153,8 @@ public class BatteryController extends StateController { // Charging/not charging. filter.addAction(Intent.ACTION_POWER_CONNECTED); filter.addAction(Intent.ACTION_POWER_DISCONNECTED); + // Charging stable. + filter.addAction(ACTION_CHARGING_STABLE); mContext.registerReceiver(this, filter); // Initialise tracker state. @@ -189,6 +197,10 @@ public class BatteryController extends StateController { mBatteryHealthy = true; maybeReportNewChargingState(); } else if (Intent.ACTION_POWER_CONNECTED.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Received charging intent, setting alarm for " + + STABLE_CHARGING_THRESHOLD_MILLIS); + } // Set up an alarm for ACTION_CHARGING_STABLE - we don't want to kick off tasks // here if the user unplugs the phone immediately. mAlarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, @@ -196,6 +208,9 @@ public class BatteryController extends StateController { mStableChargingTriggerIntent); mCharging = true; } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Disconnected from power, cancelling any set alarms."); + } // If an alarm is set, breathe a sigh of relief and cancel it - crisis averted. mAlarm.cancel(mStableChargingTriggerIntent); mCharging = false; @@ -203,7 +218,8 @@ public class BatteryController extends StateController { }else if (ACTION_CHARGING_STABLE.equals(action)) { // Here's where we actually do the notify for a task being ready. if (DEBUG) { - Slog.d(TAG, "Battery connected fired @ " + SystemClock.elapsedRealtime()); + Slog.d(TAG, "Battery connected fired @ " + SystemClock.elapsedRealtime() + + " charging: " + mCharging); } if (mCharging) { // Should never receive this intent if mCharging is false. maybeReportNewChargingState(); @@ -214,6 +230,17 @@ public class BatteryController extends StateController { @Override public void dumpControllerState(PrintWriter pw) { - + pw.println("Batt."); + pw.println("Stable power: " + mChargeTracker.isOnStablePower()); + synchronized (mTrackedTasks) { + Iterator<JobStatus> it = mTrackedTasks.iterator(); + if (it.hasNext()) { + pw.print(String.valueOf(it.next().hashCode())); + } + while (it.hasNext()) { + pw.print("," + String.valueOf(it.next().hashCode())); + } + pw.println(); + } } } diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index c1ab0f0..daba0d9 100644 --- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import android.content.BroadcastReceiver; @@ -28,8 +28,8 @@ import android.os.UserHandle; import android.util.Slog; import com.android.server.ConnectivityService; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; import java.io.PrintWriter; import java.util.LinkedList; @@ -42,9 +42,9 @@ import java.util.List; */ public class ConnectivityController extends StateController implements ConnectivityManager.OnNetworkActiveListener { - private static final String TAG = "TaskManager.Conn"; + private static final String TAG = "JobScheduler.Conn"; - private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>(); + private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>(); private final BroadcastReceiver mConnectivityChangedReceiver = new ConnectivityChangedReceiver(); /** Singleton. */ @@ -55,10 +55,10 @@ public class ConnectivityController extends StateController implements /** Track whether the latest active network is connected. */ private boolean mNetworkConnected; - public static ConnectivityController get(TaskManagerService taskManager) { + public static ConnectivityController get(JobSchedulerService jms) { synchronized (sCreationLock) { if (mSingleton == null) { - mSingleton = new ConnectivityController(taskManager, taskManager.getContext()); + mSingleton = new ConnectivityController(jms, jms.getContext()); } return mSingleton; } @@ -82,21 +82,21 @@ public class ConnectivityController extends StateController implements } @Override - public void maybeStartTrackingTask(TaskStatus taskStatus) { - if (taskStatus.hasConnectivityConstraint() || taskStatus.hasUnmeteredConstraint()) { - synchronized (mTrackedTasks) { - taskStatus.connectivityConstraintSatisfied.set(mNetworkConnected); - taskStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered); - mTrackedTasks.add(taskStatus); + public void maybeStartTrackingJob(JobStatus jobStatus) { + if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) { + synchronized (mTrackedJobs) { + jobStatus.connectivityConstraintSatisfied.set(mNetworkConnected); + jobStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered); + mTrackedJobs.add(jobStatus); } } } @Override - public void maybeStopTrackingTask(TaskStatus taskStatus) { - if (taskStatus.hasConnectivityConstraint() || taskStatus.hasUnmeteredConstraint()) { - synchronized (mTrackedTasks) { - mTrackedTasks.remove(taskStatus); + public void maybeStopTrackingJob(JobStatus jobStatus) { + if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) { + synchronized (mTrackedJobs) { + mTrackedJobs.remove(jobStatus); } } } @@ -104,16 +104,16 @@ public class ConnectivityController extends StateController implements /** * @param userId Id of the user for whom we are updating the connectivity state. */ - private void updateTrackedTasks(int userId) { - synchronized (mTrackedTasks) { + private void updateTrackedJobs(int userId) { + synchronized (mTrackedJobs) { boolean changed = false; - for (TaskStatus ts : mTrackedTasks) { - if (ts.getUserId() != userId) { + for (JobStatus js : mTrackedJobs) { + if (js.getUserId() != userId) { continue; } boolean prevIsConnected = - ts.connectivityConstraintSatisfied.getAndSet(mNetworkConnected); - boolean prevIsMetered = ts.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered); + js.connectivityConstraintSatisfied.getAndSet(mNetworkConnected); + boolean prevIsMetered = js.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered); if (prevIsConnected != mNetworkConnected || prevIsMetered != mNetworkUnmetered) { changed = true; } @@ -125,16 +125,16 @@ public class ConnectivityController extends StateController implements } /** - * We know the network has just come up. We want to run any tasks that are ready. + * We know the network has just come up. We want to run any jobs that are ready. */ public synchronized void onNetworkActive() { - synchronized (mTrackedTasks) { - for (TaskStatus ts : mTrackedTasks) { - if (ts.isReady()) { + synchronized (mTrackedJobs) { + for (JobStatus js : mTrackedJobs) { + if (js.isReady()) { if (DEBUG) { - Slog.d(TAG, "Running " + ts + " due to network activity."); + Slog.d(TAG, "Running " + js + " due to network activity."); } - mStateChangedListener.onRunTaskNow(ts); + mStateChangedListener.onRunJobNow(js); } } } @@ -169,7 +169,7 @@ public class ConnectivityController extends StateController implements if (activeNetwork == null) { mNetworkUnmetered = false; mNetworkConnected = false; - updateTrackedTasks(userid); + updateTrackedJobs(userid); } else if (activeNetwork.getType() == networkType) { mNetworkUnmetered = false; mNetworkConnected = !intent.getBooleanExtra( @@ -177,7 +177,7 @@ public class ConnectivityController extends StateController implements if (mNetworkConnected) { // No point making the call if we know there's no conn. mNetworkUnmetered = !connManager.isActiveNetworkMetered(); } - updateTrackedTasks(userid); + updateTrackedJobs(userid); } } else { if (DEBUG) { @@ -191,10 +191,10 @@ public class ConnectivityController extends StateController implements public void dumpControllerState(PrintWriter pw) { pw.println("Conn."); pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered); - for (TaskStatus ts: mTrackedTasks) { - pw.println(String.valueOf(ts.hashCode()).substring(0, 3) + ".." - + ": C=" + ts.hasConnectivityConstraint() - + ", UM=" + ts.hasUnmeteredConstraint()); + for (JobStatus js: mTrackedJobs) { + pw.println(String.valueOf(js.hashCode()).substring(0, 3) + ".." + + ": C=" + js.hasConnectivityConstraint() + + ", UM=" + js.hasUnmeteredConstraint()); } } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/task/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index e749b00..07ffe4d 100644 --- a/services/core/java/com/android/server/task/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import java.io.PrintWriter; import java.util.ArrayList; @@ -29,8 +29,8 @@ import android.content.IntentFilter; import android.os.SystemClock; import android.util.Slog; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; public class IdleController extends StateController { private static final String TAG = "IdleController"; @@ -43,14 +43,14 @@ public class IdleController extends StateController { private static final String ACTION_TRIGGER_IDLE = "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE"; - final ArrayList<TaskStatus> mTrackedTasks = new ArrayList<TaskStatus>(); + final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>(); IdlenessTracker mIdleTracker; // Singleton factory private static Object sCreationLock = new Object(); private static volatile IdleController sController; - public static IdleController get(TaskManagerService service) { + public static IdleController get(JobSchedulerService service) { synchronized (sCreationLock) { if (sController == null) { sController = new IdleController(service, service.getContext()); @@ -68,7 +68,7 @@ public class IdleController extends StateController { * StateController interface */ @Override - public void maybeStartTrackingTask(TaskStatus taskStatus) { + public void maybeStartTrackingJob(JobStatus taskStatus) { if (taskStatus.hasIdleConstraint()) { synchronized (mTrackedTasks) { mTrackedTasks.add(taskStatus); @@ -78,7 +78,7 @@ public class IdleController extends StateController { } @Override - public void maybeStopTrackingTask(TaskStatus taskStatus) { + public void maybeStopTrackingJob(JobStatus taskStatus) { synchronized (mTrackedTasks) { mTrackedTasks.remove(taskStatus); } @@ -89,7 +89,7 @@ public class IdleController extends StateController { */ void reportNewIdleState(boolean isIdle) { synchronized (mTrackedTasks) { - for (TaskStatus task : mTrackedTasks) { + for (JobStatus task : mTrackedTasks) { task.idleConstraintSatisfied.set(isIdle); } } diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index a286737..15a6b25 100644 --- a/services/core/java/com/android/server/task/controllers/TaskStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -14,9 +14,9 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; -import android.app.task.Task; +import android.app.job.JobInfo; import android.content.ComponentName; import android.os.PersistableBundle; import android.os.SystemClock; @@ -26,9 +26,9 @@ import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicBoolean; /** - * Uniquely identifies a task internally. - * Created from the public {@link android.app.task.Task} object when it lands on the scheduler. - * Contains current state of the requirements of the task, as well as a function to evaluate + * Uniquely identifies a job internally. + * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler. + * Contains current state of the requirements of the job, as well as a function to evaluate * whether it's ready to run. * This object is shared among the various controllers - hence why the different fields are atomic. * This isn't strictly necessary because each controller is only interested in a specific field, @@ -36,14 +36,14 @@ import java.util.concurrent.atomic.AtomicBoolean; * but we don't enforce that so this is safer. * @hide */ -public class TaskStatus { +public class JobStatus { public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; - final Task task; + final JobInfo job; final int uId; - /** At reschedule time we need to know whether to update task on disk. */ + /** At reschedule time we need to know whether to update job on disk. */ final boolean persisted; // Constraints. @@ -55,77 +55,77 @@ public class TaskStatus { final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean(); /** - * Earliest point in the future at which this task will be eligible to run. A value of 0 + * Earliest point in the future at which this job will be eligible to run. A value of 0 * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}. */ private long earliestRunTimeElapsedMillis; /** - * Latest point in the future at which this task must be run. A value of {@link Long#MAX_VALUE} + * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE} * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}. */ private long latestRunTimeElapsedMillis; - /** How many times this task has failed, used to compute back-off. */ + /** How many times this job has failed, used to compute back-off. */ private final int numFailures; - /** Provide a handle to the service that this task will be run on. */ + /** Provide a handle to the service that this job will be run on. */ public int getServiceToken() { return uId; } - private TaskStatus(Task task, int uId, boolean persisted, int numFailures) { - this.task = task; + private JobStatus(JobInfo job, int uId, boolean persisted, int numFailures) { + this.job = job; this.uId = uId; this.numFailures = numFailures; this.persisted = persisted; } - /** Create a newly scheduled task. */ - public TaskStatus(Task task, int uId, boolean persisted) { - this(task, uId, persisted, 0); + /** Create a newly scheduled job. */ + public JobStatus(JobInfo job, int uId, boolean persisted) { + this(job, uId, persisted, 0); final long elapsedNow = SystemClock.elapsedRealtime(); - if (task.isPeriodic()) { + if (job.isPeriodic()) { earliestRunTimeElapsedMillis = elapsedNow; - latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis(); + latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis(); } else { - earliestRunTimeElapsedMillis = task.hasEarlyConstraint() ? - elapsedNow + task.getMinLatencyMillis() : NO_EARLIEST_RUNTIME; - latestRunTimeElapsedMillis = task.hasLateConstraint() ? - elapsedNow + task.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME; + earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ? + elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME; + latestRunTimeElapsedMillis = job.hasLateConstraint() ? + elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME; } } /** - * Create a new TaskStatus that was loaded from disk. We ignore the provided - * {@link android.app.task.Task} time criteria because we can load a persisted periodic task - * from the {@link com.android.server.task.TaskStore} and still want to respect its + * Create a new JobStatus that was loaded from disk. We ignore the provided + * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job + * from the {@link com.android.server.job.JobStore} and still want to respect its * wallclock runtime rather than resetting it on every boot. - * We consider a freshly loaded task to no longer be in back-off. + * We consider a freshly loaded job to no longer be in back-off. */ - public TaskStatus(Task task, int uId, long earliestRunTimeElapsedMillis, + public JobStatus(JobInfo job, int uId, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) { - this(task, uId, true, 0); + this(job, uId, true, 0); this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis; this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis; } - /** Create a new task to be rescheduled with the provided parameters. */ - public TaskStatus(TaskStatus rescheduling, long newEarliestRuntimeElapsedMillis, + /** Create a new job to be rescheduled with the provided parameters. */ + public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int backoffAttempt) { - this(rescheduling.task, rescheduling.getUid(), rescheduling.isPersisted(), backoffAttempt); + this(rescheduling.job, rescheduling.getUid(), rescheduling.isPersisted(), backoffAttempt); earliestRunTimeElapsedMillis = newEarliestRuntimeElapsedMillis; latestRunTimeElapsedMillis = newLatestRuntimeElapsedMillis; } - public Task getTask() { - return task; + public JobInfo getJob() { + return job; } - public int getTaskId() { - return task.getId(); + public int getJobId() { + return job.getId(); } public int getNumFailures() { @@ -133,7 +133,7 @@ public class TaskStatus { } public ComponentName getServiceComponent() { - return task.getService(); + return job.getService(); } public int getUserId() { @@ -145,19 +145,19 @@ public class TaskStatus { } public PersistableBundle getExtras() { - return task.getExtras(); + return job.getExtras(); } public boolean hasConnectivityConstraint() { - return task.getNetworkCapabilities() == Task.NetworkType.ANY; + return job.getNetworkCapabilities() == JobInfo.NetworkType.ANY; } public boolean hasUnmeteredConstraint() { - return task.getNetworkCapabilities() == Task.NetworkType.UNMETERED; + return job.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED; } public boolean hasChargingConstraint() { - return task.isRequireCharging(); + return job.isRequireCharging(); } public boolean hasTimingDelayConstraint() { @@ -169,7 +169,7 @@ public class TaskStatus { } public boolean hasIdleConstraint() { - return task.isRequireDeviceIdle(); + return job.isRequireDeviceIdle(); } public long getEarliestRunTime() { @@ -184,7 +184,7 @@ public class TaskStatus { return persisted; } /** - * @return Whether or not this task is ready to run, based on its requirements. + * @return Whether or not this job is ready to run, based on its requirements. */ public synchronized boolean isReady() { return (!hasChargingConstraint() || chargingConstraintSatisfied.get()) @@ -196,36 +196,17 @@ public class TaskStatus { || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get()); } - /*@Override - public int hashCode() { - int result = getServiceComponent().hashCode(); - result = 31 * result + task.getId(); - result = 31 * result + uId; - return result; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TaskStatus)) return false; - - TaskStatus that = (TaskStatus) o; - return ((task.getId() == that.task.getId()) - && (uId == that.uId) - && (getServiceComponent().equals(that.getServiceComponent()))); - }*/ - - public boolean matches(int uid, int taskId) { - return this.task.getId() == taskId && this.uId == uid; + public boolean matches(int uid, int jobId) { + return this.job.getId() == jobId && this.uId == uid; } @Override public String toString() { return String.valueOf(hashCode()).substring(0, 3) + ".." - + ":[" + task.getService().getPackageName() + ",tId=" + task.getId() + + ":[" + job.getService().getPackageName() + ",jId=" + job.getId() + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")" - + ",N=" + task.getNetworkCapabilities() + ",C=" + task.isRequireCharging() - + ",I=" + task.isRequireDeviceIdle() + ",F=" + numFailures + + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging() + + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures + (isReady() ? "(READY)" : "") + "]"; } diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index a7f52f5..81658bf 100644 --- a/services/core/java/com/android/server/task/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -14,18 +14,18 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import android.content.Context; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; import java.io.PrintWriter; /** - * Incorporates shared controller logic between the various controllers of the TaskManager. - * These are solely responsible for tracking a list of tasks, and notifying the TM when these + * Incorporates shared controller logic between the various controllers of the JobManager. + * These are solely responsible for tracking a list of jobs, and notifying the JM when these * are ready to run, or whether they must be stopped. */ public abstract class StateController { @@ -39,16 +39,16 @@ public abstract class StateController { } /** - * Implement the logic here to decide whether a task should be tracked by this controller. - * This logic is put here so the TaskManger can be completely agnostic of Controller logic. + * Implement the logic here to decide whether a job should be tracked by this controller. + * This logic is put here so the JobManger can be completely agnostic of Controller logic. * Also called when updating a task, so implementing controllers have to be aware of * preexisting tasks. */ - public abstract void maybeStartTrackingTask(TaskStatus taskStatus); + public abstract void maybeStartTrackingJob(JobStatus jobStatus); /** * Remove task - this will happen if the task is cancelled, completed, etc. */ - public abstract void maybeStopTrackingTask(TaskStatus taskStatus); + public abstract void maybeStopTrackingJob(JobStatus jobStatus); public abstract void dumpControllerState(PrintWriter pw); diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index b75036c..e46226c 100644 --- a/services/core/java/com/android/server/task/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.server.task.controllers; +package com.android.server.job.controllers; import android.app.AlarmManager; import android.app.PendingIntent; @@ -25,8 +25,8 @@ import android.content.IntentFilter; import android.os.SystemClock; import android.util.Slog; -import com.android.server.task.StateChangedListener; -import com.android.server.task.TaskManagerService; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.StateChangedListener; import java.io.PrintWriter; import java.util.Iterator; @@ -35,35 +35,35 @@ import java.util.List; import java.util.ListIterator; /** - * This class sets an alarm for the next expiring task, and determines whether a task's minimum + * This class sets an alarm for the next expiring job, and determines whether a job's minimum * delay has been satisfied. */ public class TimeController extends StateController { - private static final String TAG = "TaskManager.Time"; - private static final String ACTION_TASK_EXPIRED = - "android.content.taskmanager.TASK_DEADLINE_EXPIRED"; - private static final String ACTION_TASK_DELAY_EXPIRED = - "android.content.taskmanager.TASK_DELAY_EXPIRED"; + private static final String TAG = "JobScheduler.Time"; + private static final String ACTION_JOB_EXPIRED = + "android.content.jobscheduler.JOB_DEADLINE_EXPIRED"; + private static final String ACTION_JOB_DELAY_EXPIRED = + "android.content.jobscheduler.JOB_DELAY_EXPIRED"; - /** Set an alarm for the next task expiry. */ + /** Set an alarm for the next job expiry. */ private final PendingIntent mDeadlineExpiredAlarmIntent; - /** Set an alarm for the next task delay expiry. This*/ + /** Set an alarm for the next job delay expiry. This*/ private final PendingIntent mNextDelayExpiredAlarmIntent; /** Constant time determining how near in the future we'll set an alarm for. */ private static final long MIN_WAKEUP_INTERVAL_MILLIS = 15 * 1000; - private long mNextTaskExpiredElapsedMillis; + private long mNextJobExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; private AlarmManager mAlarmService = null; - /** List of tracked tasks, sorted asc. by deadline */ - private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>(); + /** List of tracked jobs, sorted asc. by deadline */ + private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>(); /** Singleton. */ private static TimeController mSingleton; - public static synchronized TimeController get(TaskManagerService taskManager) { + public static synchronized TimeController get(JobSchedulerService jms) { if (mSingleton == null) { - mSingleton = new TimeController(taskManager, taskManager.getContext()); + mSingleton = new TimeController(jms, jms.getContext()); } return mSingleton; } @@ -72,66 +72,66 @@ public class TimeController extends StateController { super(stateChangedListener, context); mDeadlineExpiredAlarmIntent = PendingIntent.getBroadcast(mContext, 0 /* ignored */, - new Intent(ACTION_TASK_EXPIRED), 0); + new Intent(ACTION_JOB_EXPIRED), 0); mNextDelayExpiredAlarmIntent = PendingIntent.getBroadcast(mContext, 0 /* ignored */, - new Intent(ACTION_TASK_DELAY_EXPIRED), 0); - mNextTaskExpiredElapsedMillis = Long.MAX_VALUE; + new Intent(ACTION_JOB_DELAY_EXPIRED), 0); + mNextJobExpiredElapsedMillis = Long.MAX_VALUE; mNextDelayExpiredElapsedMillis = Long.MAX_VALUE; // Register BR for these intents. - IntentFilter intentFilter = new IntentFilter(ACTION_TASK_EXPIRED); - intentFilter.addAction(ACTION_TASK_DELAY_EXPIRED); + IntentFilter intentFilter = new IntentFilter(ACTION_JOB_EXPIRED); + intentFilter.addAction(ACTION_JOB_DELAY_EXPIRED); mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter); } /** - * Check if the task has a timing constraint, and if so determine where to insert it in our + * Check if the job has a timing constraint, and if so determine where to insert it in our * list. */ @Override - public synchronized void maybeStartTrackingTask(TaskStatus task) { - if (task.hasTimingDelayConstraint() || task.hasDeadlineConstraint()) { - maybeStopTrackingTask(task); - ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size()); + public synchronized void maybeStartTrackingJob(JobStatus job) { + if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) { + maybeStopTrackingJob(job); + ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size()); while (it.hasPrevious()) { - TaskStatus ts = it.previous(); - if (ts.getLatestRunTimeElapsed() < task.getLatestRunTimeElapsed()) { + JobStatus ts = it.previous(); + if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) { // Insert break; } } - it.add(task); + it.add(job); maybeUpdateAlarms( - task.hasTimingDelayConstraint() ? task.getEarliestRunTime() : Long.MAX_VALUE, - task.hasDeadlineConstraint() ? task.getLatestRunTimeElapsed() : Long.MAX_VALUE); + job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE, + job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE); } } /** - * When we stop tracking a task, we only need to update our alarms if the task we're no longer + * When we stop tracking a job, we only need to update our alarms if the job we're no longer * tracking was the one our alarms were based off of. * Really an == comparison should be enough, but why play with fate? We'll do <=. */ @Override - public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) { - if (mTrackedTasks.remove(taskStatus)) { + public synchronized void maybeStopTrackingJob(JobStatus job) { + if (mTrackedJobs.remove(job)) { checkExpiredDelaysAndResetAlarm(); checkExpiredDeadlinesAndResetAlarm(); } } /** - * Determines whether this controller can stop tracking the given task. - * The controller is no longer interested in a task once its time constraint is satisfied, and - * the task's deadline is fulfilled - unlike other controllers a time constraint can't toggle + * Determines whether this controller can stop tracking the given job. + * The controller is no longer interested in a job once its time constraint is satisfied, and + * the job's deadline is fulfilled - unlike other controllers a time constraint can't toggle * back and forth. */ - private boolean canStopTrackingTask(TaskStatus taskStatus) { - return (!taskStatus.hasTimingDelayConstraint() || - taskStatus.timeDelayConstraintSatisfied.get()) && - (!taskStatus.hasDeadlineConstraint() || - taskStatus.deadlineConstraintSatisfied.get()); + private boolean canStopTrackingJob(JobStatus job) { + return (!job.hasTimingDelayConstraint() || + job.timeDelayConstraintSatisfied.get()) && + (!job.hasDeadlineConstraint() || + job.deadlineConstraintSatisfied.get()); } private void ensureAlarmService() { @@ -141,27 +141,27 @@ public class TimeController extends StateController { } /** - * Checks list of tasks for ones that have an expired deadline, sending them to the TaskManager + * Checks list of jobs for ones that have an expired deadline, sending them to the JobScheduler * if so, removing them from this list, and updating the alarm for the next expiry time. */ private synchronized void checkExpiredDeadlinesAndResetAlarm() { long nextExpiryTime = Long.MAX_VALUE; final long nowElapsedMillis = SystemClock.elapsedRealtime(); - Iterator<TaskStatus> it = mTrackedTasks.iterator(); + Iterator<JobStatus> it = mTrackedJobs.iterator(); while (it.hasNext()) { - TaskStatus ts = it.next(); - if (!ts.hasDeadlineConstraint()) { + JobStatus job = it.next(); + if (!job.hasDeadlineConstraint()) { continue; } - final long taskDeadline = ts.getLatestRunTimeElapsed(); + final long jobDeadline = job.getLatestRunTimeElapsed(); - if (taskDeadline <= nowElapsedMillis) { - ts.deadlineConstraintSatisfied.set(true); - mStateChangedListener.onRunTaskNow(ts); + if (jobDeadline <= nowElapsedMillis) { + job.deadlineConstraintSatisfied.set(true); + mStateChangedListener.onRunJobNow(job); it.remove(); } else { // Sorted by expiry time, so take the next one and stop. - nextExpiryTime = taskDeadline; + nextExpiryTime = jobDeadline; break; } } @@ -169,31 +169,31 @@ public class TimeController extends StateController { } /** - * Handles alarm that notifies us that a task's delay has expired. Iterates through the list of - * tracked tasks and marks them as ready as appropriate. + * Handles alarm that notifies us that a job's delay has expired. Iterates through the list of + * tracked jobs and marks them as ready as appropriate. */ private synchronized void checkExpiredDelaysAndResetAlarm() { final long nowElapsedMillis = SystemClock.elapsedRealtime(); long nextDelayTime = Long.MAX_VALUE; boolean ready = false; - Iterator<TaskStatus> it = mTrackedTasks.iterator(); + Iterator<JobStatus> it = mTrackedJobs.iterator(); while (it.hasNext()) { - final TaskStatus ts = it.next(); - if (!ts.hasTimingDelayConstraint()) { + final JobStatus job = it.next(); + if (!job.hasTimingDelayConstraint()) { continue; } - final long taskDelayTime = ts.getEarliestRunTime(); - if (taskDelayTime <= nowElapsedMillis) { - ts.timeDelayConstraintSatisfied.set(true); - if (canStopTrackingTask(ts)) { + final long jobDelayTime = job.getEarliestRunTime(); + if (jobDelayTime <= nowElapsedMillis) { + job.timeDelayConstraintSatisfied.set(true); + if (canStopTrackingJob(job)) { it.remove(); } - if (ts.isReady()) { + if (job.isReady()) { ready = true; } } else { // Keep going through list to get next delay time. - if (nextDelayTime > taskDelayTime) { - nextDelayTime = taskDelayTime; + if (nextDelayTime > jobDelayTime) { + nextDelayTime = jobDelayTime; } } } @@ -207,13 +207,13 @@ public class TimeController extends StateController { if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) { setDelayExpiredAlarm(delayExpiredElapsed); } - if (deadlineExpiredElapsed < mNextTaskExpiredElapsedMillis) { + if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) { setDeadlineExpiredAlarm(deadlineExpiredElapsed); } } /** - * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's + * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's * delay will expire. * This alarm <b>will not</b> wake up the phone. */ @@ -228,7 +228,7 @@ public class TimeController extends StateController { } /** - * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's + * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's * deadline will expire. * This alarm <b>will</b> wake up the phone. */ @@ -238,8 +238,8 @@ public class TimeController extends StateController { if (alarmTimeElapsedMillis < earliestWakeupTimeElapsed) { alarmTimeElapsedMillis = earliestWakeupTimeElapsed; } - mNextTaskExpiredElapsedMillis = alarmTimeElapsedMillis; - updateAlarmWithPendingIntent(mDeadlineExpiredAlarmIntent, mNextTaskExpiredElapsedMillis); + mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis; + updateAlarmWithPendingIntent(mDeadlineExpiredAlarmIntent, mNextJobExpiredElapsedMillis); } private void updateAlarmWithPendingIntent(PendingIntent pi, long alarmTimeElapsed) { @@ -260,11 +260,11 @@ public class TimeController extends StateController { if (DEBUG) { Slog.d(TAG, "Just received alarm: " + intent.getAction()); } - // An task has just expired, so we run through the list of tasks that we have and + // A job has just expired, so we run through the list of jobs that we have and // notify our StateChangedListener. - if (ACTION_TASK_EXPIRED.equals(intent.getAction())) { + if (ACTION_JOB_EXPIRED.equals(intent.getAction())) { checkExpiredDeadlinesAndResetAlarm(); - } else if (ACTION_TASK_DELAY_EXPIRED.equals(intent.getAction())) { + } else if (ACTION_JOB_DELAY_EXPIRED.equals(intent.getAction())) { checkExpiredDelaysAndResetAlarm(); } } @@ -276,10 +276,10 @@ public class TimeController extends StateController { pw.println("Alarms (" + SystemClock.elapsedRealtime() + ")"); pw.println( "Next delay alarm in " + (mNextDelayExpiredElapsedMillis - nowElapsed)/1000 + "s"); - pw.println("Next deadline alarm in " + (mNextTaskExpiredElapsedMillis - nowElapsed)/1000 + pw.println("Next deadline alarm in " + (mNextJobExpiredElapsedMillis - nowElapsed)/1000 + "s"); pw.println("Tracking:"); - for (TaskStatus ts : mTrackedTasks) { + for (JobStatus ts : mTrackedJobs) { pw.println(String.valueOf(ts.hashCode()).substring(0, 3) + ".." + ": (" + (ts.hasTimingDelayConstraint() ? ts.getEarliestRunTime() : "N/A") + ", " + (ts.hasDeadlineConstraint() ?ts.getLatestRunTimeElapsed() : "N/A") diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 835b094..9ae8aed 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -949,6 +949,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override + public long getFlags() { + return mFlags; + } + + @Override public void play() throws RemoteException { mSessionCb.play(); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 386402b..06732ca 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -188,6 +188,8 @@ public class NotificationManagerService extends SystemService { private AppOpsManager mAppOps; + private Archive mArchive; + // Notification control database. For now just contains disabled packages. private AtomicFile mPolicyFile; private HashSet<String> mBlockedPackages = new HashSet<String>(); @@ -223,10 +225,12 @@ public class NotificationManagerService extends SystemService { private static final int REASON_LISTENER_CANCEL_ALL = 11; private static class Archive { - static final int BUFFER_SIZE = 250; - ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE); + final int mBufferSize; + final ArrayDeque<StatusBarNotification> mBuffer; - public Archive() { + public Archive(int size) { + mBufferSize = size; + mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize); } public String toString() { @@ -240,7 +244,7 @@ public class NotificationManagerService extends SystemService { } public void record(StatusBarNotification nr) { - if (mBuffer.size() == BUFFER_SIZE) { + if (mBuffer.size() == mBufferSize) { mBuffer.removeFirst(); } @@ -250,7 +254,6 @@ public class NotificationManagerService extends SystemService { mBuffer.addLast(nr.cloneLight()); } - public void clear() { mBuffer.clear(); } @@ -300,7 +303,7 @@ public class NotificationManagerService extends SystemService { } public StatusBarNotification[] getArray(int count) { - if (count == 0) count = Archive.BUFFER_SIZE; + if (count == 0) count = mBufferSize; final StatusBarNotification[] a = new StatusBarNotification[Math.min(count, mBuffer.size())]; Iterator<StatusBarNotification> iter = descendingIterator(); @@ -312,7 +315,7 @@ public class NotificationManagerService extends SystemService { } public StatusBarNotification[] getArray(int count, String pkg, int userId) { - if (count == 0) count = Archive.BUFFER_SIZE; + if (count == 0) count = mBufferSize; final StatusBarNotification[] a = new StatusBarNotification[Math.min(count, mBuffer.size())]; Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId); @@ -325,8 +328,6 @@ public class NotificationManagerService extends SystemService { } - Archive mArchive = new Archive(); - private void loadPolicyFile() { synchronized(mPolicyFile) { mBlockedPackages.clear(); @@ -854,6 +855,9 @@ public class NotificationManagerService extends SystemService { } } + mArchive = new Archive(resources.getInteger( + R.integer.config_notificationServiceArchiveSize)); + publishBinderService(Context.NOTIFICATION_SERVICE, mService); publishLocalService(NotificationManagerInternal.class, mInternalService); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 50a32c4..b95db9c 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -18,6 +18,7 @@ package com.android.server.notification; import android.app.AlarmManager; import android.app.AppOpsManager; +import android.app.Notification; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -88,6 +89,10 @@ public class ZenModeHelper { private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList( "com.google.android.deskclock" )); + private static final Set<String> SYSTEM_PACKAGES = new HashSet<String>(Arrays.asList( + "android", + "com.android.systemui" + )); public ZenModeHelper(Context context, Handler handler) { mContext = context; @@ -130,6 +135,9 @@ public class ZenModeHelper { // notifications never transition from not intercepted to intercepted return false; } + if (isSystem(record)) { + return false; + } if (isAlarm(record)) { return false; } @@ -239,6 +247,11 @@ public class ZenModeHelper { } } + private boolean isSystem(NotificationRecord record) { + return SYSTEM_PACKAGES.contains(record.sbn.getPackageName()) + && Notification.CATEGORY_SYSTEM.equals(record.getNotification().category); + } + private boolean isAlarm(NotificationRecord record) { return ALARM_PACKAGES.contains(record.sbn.getPackageName()); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8585b4e..49502b6 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4991,6 +4991,7 @@ public class PackageManagerService extends IPackageManager.Stub { mResolveActivity.packageName = mAndroidApplication.packageName; mResolveActivity.processName = "system:ui"; mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; + mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert; mResolveActivity.exported = true; @@ -10265,7 +10266,7 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { // Check whether the newly-scanned package wants to define an already-defined perm int N = pkg.permissions.size(); - for (int i = 0; i < N; i++) { + for (int i = N-1; i >= 0; i--) { PackageParser.Permission perm = pkg.permissions.get(i); BasePermission bp = mSettings.mPermissions.get(perm.info.name); if (bp != null) { @@ -10273,13 +10274,23 @@ public class PackageManagerService extends IPackageManager.Stub { // also includes the "updating the same package" case, of course. if (compareSignatures(bp.packageSetting.signatures.mSignatures, pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { - Slog.w(TAG, "Package " + pkg.packageName - + " attempting to redeclare permission " + perm.info.name - + " already owned by " + bp.sourcePackage); - res.returnCode = PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION; - res.origPermission = perm.info.name; - res.origPackage = bp.sourcePackage; - return; + // If the owning package is the system itself, we log but allow + // install to proceed; we fail the install on all other permission + // redefinitions. + if (!bp.sourcePackage.equals("android")) { + Slog.w(TAG, "Package " + pkg.packageName + + " attempting to redeclare permission " + perm.info.name + + " already owned by " + bp.sourcePackage); + res.returnCode = PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION; + res.origPermission = perm.info.name; + res.origPackage = bp.sourcePackage; + return; + } else { + Slog.w(TAG, "Package " + pkg.packageName + + " attempting to redeclare system permission " + + perm.info.name + "; ignoring new declaration"); + pkg.permissions.remove(i); + } } } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b7bc2e1..0cb2ab9 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -161,7 +161,6 @@ public class UserManagerService extends IUserManager.Stub { private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray(); private int[] mUserIds; - private boolean mGuestEnabled; private int mNextSerialNumber; private int mUserVersion = 0; @@ -427,43 +426,6 @@ public class UserManagerService extends IUserManager.Stub { } } - @Override - public void setGuestEnabled(boolean enable) { - checkManageUsersPermission("enable guest users"); - synchronized (mPackagesLock) { - if (mGuestEnabled != enable) { - mGuestEnabled = enable; - // Erase any guest user that currently exists - for (int i = 0; i < mUsers.size(); i++) { - UserInfo user = mUsers.valueAt(i); - if (!user.partial && user.isGuest()) { - if (!enable) { - removeUser(user.id); - } - return; - } - } - // No guest was found - if (enable) { - createUser("Guest", UserInfo.FLAG_GUEST); - } - } - } - } - - @Override - public boolean isGuestEnabled() { - synchronized (mPackagesLock) { - return mGuestEnabled; - } - } - - @Override - public void wipeUser(int userHandle) { - checkManageUsersPermission("wipe user"); - // TODO: - } - public void makeInitialized(int userId) { checkManageUsersPermission("makeInitialized"); synchronized (mPackagesLock) { @@ -583,7 +545,6 @@ public class UserManagerService extends IUserManager.Stub { } private void readUserListLocked() { - mGuestEnabled = false; if (!mUserListFile.exists()) { fallbackToSingleUserLocked(); return; @@ -625,9 +586,6 @@ public class UserManagerService extends IUserManager.Stub { if (user != null) { mUsers.put(user.id, user); - if (user.isGuest()) { - mGuestEnabled = true; - } if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) { mNextSerialNumber = user.id + 1; } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index d8671d9..fb4b8f0 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -409,6 +409,9 @@ public final class PowerManagerService extends com.android.server.SystemService // Current state of the low power mode setting. private boolean mLowPowerModeSetting; + // Current state of whether the settings are allowing auto low power mode. + private boolean mAutoLowPowerModeEnabled; + // True if the battery level is currently considered low. private boolean mBatteryLevelLow; @@ -558,6 +561,9 @@ public final class PowerManagerService extends com.android.server.SystemService resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.LOW_POWER_MODE), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), + false, mSettingsObserver, UserHandle.USER_ALL); // Go. readConfigurationLocked(); updateSettingsLocked(); @@ -645,8 +651,12 @@ public final class PowerManagerService extends com.android.server.SystemService final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver, Settings.Global.LOW_POWER_MODE, 0) != 0; - if (lowPowerModeEnabled != mLowPowerModeSetting) { + final boolean autoLowPowerModeEnabled = Settings.Global.getInt(resolver, + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 15) != 0; + if (lowPowerModeEnabled != mLowPowerModeSetting + || autoLowPowerModeEnabled != mAutoLowPowerModeEnabled) { mLowPowerModeSetting = lowPowerModeEnabled; + mAutoLowPowerModeEnabled = autoLowPowerModeEnabled; updateLowPowerModeLocked(); } @@ -654,7 +664,8 @@ public final class PowerManagerService extends com.android.server.SystemService } void updateLowPowerModeLocked() { - final boolean lowPowerModeEnabled = mLowPowerModeSetting || mBatteryLevelLow; + final boolean lowPowerModeEnabled = !mIsPowered + && (mLowPowerModeSetting || (mAutoLowPowerModeEnabled && mBatteryLevelLow)); if (mLowPowerModeEnabled != lowPowerModeEnabled) { mLowPowerModeEnabled = lowPowerModeEnabled; powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0); @@ -1197,7 +1208,7 @@ public final class PowerManagerService extends com.android.server.SystemService } } - if (oldLevelLow != mBatteryLevelLow) { + if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) { updateLowPowerModeLocked(); } } @@ -2168,6 +2179,8 @@ public final class PowerManagerService extends com.android.server.SystemService pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); pw.println(" mSandmanScheduled=" + mSandmanScheduled); pw.println(" mSandmanSummoned=" + mSandmanSummoned); + pw.println(" mLowPowerModeEnabled=" + mLowPowerModeEnabled); + pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime)); pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime)); pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime)); @@ -2204,6 +2217,8 @@ public final class PowerManagerService extends com.android.server.SystemService pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting); pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting); + pw.println(" mLowPowerModeSetting=" + mLowPowerModeSetting); + pw.println(" mAutoLowPowerModeEnabled=" + mAutoLowPowerModeEnabled); pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig); pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig); pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig); diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java deleted file mode 100644 index 0c55a1d..0000000 --- a/services/core/java/com/android/server/task/TaskManagerService.java +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Copyright (C) 2014 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.task; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import android.app.task.ITaskManager; -import android.app.task.Task; -import android.app.task.TaskManager; -import android.content.BroadcastReceiver; -import android.app.task.TaskService; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.ServiceInfo; -import android.os.Binder; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.Slog; -import android.util.SparseArray; - -import com.android.server.task.controllers.BatteryController; -import com.android.server.task.controllers.ConnectivityController; -import com.android.server.task.controllers.IdleController; -import com.android.server.task.controllers.StateController; -import com.android.server.task.controllers.TaskStatus; -import com.android.server.task.controllers.TimeController; - -import java.util.LinkedList; - -/** - * Responsible for taking tasks representing work to be performed by a client app, and determining - * based on the criteria specified when that task should be run against the client application's - * endpoint. - * Implements logic for scheduling, and rescheduling tasks. The TaskManagerService knows nothing - * about constraints, or the state of active tasks. It receives callbacks from the various - * controllers and completed tasks and operates accordingly. - * - * Note on locking: Any operations that manipulate {@link #mTasks} need to lock on that object. - * Any function with the suffix 'Locked' also needs to lock on {@link #mTasks}. - * @hide - */ -public class TaskManagerService extends com.android.server.SystemService - implements StateChangedListener, TaskCompletedListener, TaskMapReadFinishedListener { - // TODO: Switch this off for final version. - static final boolean DEBUG = true; - /** The number of concurrent tasks we run at one time. */ - private static final int MAX_TASK_CONTEXTS_COUNT = 3; - static final String TAG = "TaskManager"; - /** Master list of tasks. */ - private final TaskStore mTasks; - - static final int MSG_TASK_EXPIRED = 0; - static final int MSG_CHECK_TASKS = 1; - - // Policy constants - /** - * Minimum # of idle tasks that must be ready in order to force the TM to schedule things - * early. - */ - private static final int MIN_IDLE_COUNT = 1; - /** - * Minimum # of connectivity tasks that must be ready in order to force the TM to schedule - * things early. - */ - private static final int MIN_CONNECTIVITY_COUNT = 2; - /** - * Minimum # of tasks (with no particular constraints) for which the TM will be happy running - * some work early. - */ - private static final int MIN_READY_TASKS_COUNT = 4; - - /** - * Track Services that have currently active or pending tasks. The index is provided by - * {@link TaskStatus#getServiceToken()} - */ - private final List<TaskServiceContext> mActiveServices = new LinkedList<TaskServiceContext>(); - /** List of controllers that will notify this service of updates to tasks. */ - private List<StateController> mControllers; - /** - * Queue of pending tasks. The TaskServiceContext class will receive tasks from this list - * when ready to execute them. - */ - private final LinkedList<TaskStatus> mPendingTasks = new LinkedList<TaskStatus>(); - - private final TaskHandler mHandler; - private final TaskManagerStub mTaskManagerStub; - /** - * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we - * still clean up. On reinstall the package will have a new uid. - */ - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Slog.d(TAG, "Receieved: " + intent.getAction()); - if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { - int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); - if (DEBUG) { - Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); - } - cancelTasksForUid(uidRemoved); - } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - if (DEBUG) { - Slog.d(TAG, "Removing jobs for user: " + userId); - } - cancelTasksForUser(userId); - } - } - }; - - /** - * Entry point from client to schedule the provided task. - * This cancels the task if it's already been scheduled, and replaces it with the one provided. - * @param task Task object containing execution parameters - * @param uId The package identifier of the application this task is for. - * @param canPersistTask Whether or not the client has the appropriate permissions for - * persisting this task. - * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes. - */ - public int schedule(Task task, int uId, boolean canPersistTask) { - TaskStatus taskStatus = new TaskStatus(task, uId, canPersistTask); - cancelTask(uId, task.getId()); - startTrackingTask(taskStatus); - return TaskManager.RESULT_SUCCESS; - } - - public List<Task> getPendingTasks(int uid) { - ArrayList<Task> outList = new ArrayList<Task>(); - synchronized (mTasks) { - for (TaskStatus ts : mTasks.getTasks()) { - if (ts.getUid() == uid) { - outList.add(ts.getTask()); - } - } - } - return outList; - } - - private void cancelTasksForUser(int userHandle) { - synchronized (mTasks) { - List<TaskStatus> tasksForUser = mTasks.getTasksByUser(userHandle); - for (TaskStatus toRemove : tasksForUser) { - if (DEBUG) { - Slog.d(TAG, "Cancelling: " + toRemove); - } - cancelTaskLocked(toRemove); - } - } - } - - /** - * Entry point from client to cancel all tasks originating from their uid. - * This will remove the task from the master list, and cancel the task if it was staged for - * execution or being executed. - * @param uid To check against for removal of a task. - */ - public void cancelTasksForUid(int uid) { - // Remove from master list. - synchronized (mTasks) { - List<TaskStatus> tasksForUid = mTasks.getTasksByUid(uid); - for (TaskStatus toRemove : tasksForUid) { - if (DEBUG) { - Slog.d(TAG, "Cancelling: " + toRemove); - } - cancelTaskLocked(toRemove); - } - } - } - - /** - * Entry point from client to cancel the task corresponding to the taskId provided. - * This will remove the task from the master list, and cancel the task if it was staged for - * execution or being executed. - * @param uid Uid of the calling client. - * @param taskId Id of the task, provided at schedule-time. - */ - public void cancelTask(int uid, int taskId) { - TaskStatus toCancel; - synchronized (mTasks) { - toCancel = mTasks.getTaskByUidAndTaskId(uid, taskId); - if (toCancel != null) { - cancelTaskLocked(toCancel); - } - } - } - - private void cancelTaskLocked(TaskStatus cancelled) { - // Remove from store. - stopTrackingTask(cancelled); - // Remove from pending queue. - mPendingTasks.remove(cancelled); - // Cancel if running. - stopTaskOnServiceContextLocked(cancelled); - } - - /** - * Initializes the system service. - * <p> - * Subclasses must define a single argument constructor that accepts the context - * and passes it to super. - * </p> - * - * @param context The system server context. - */ - public TaskManagerService(Context context) { - super(context); - // Create the controllers. - mControllers = new LinkedList<StateController>(); - mControllers.add(ConnectivityController.get(this)); - mControllers.add(TimeController.get(this)); - mControllers.add(IdleController.get(this)); - mControllers.add(BatteryController.get(this)); - - mHandler = new TaskHandler(context.getMainLooper()); - mTaskManagerStub = new TaskManagerStub(); - // Create the "runners". - for (int i = 0; i < MAX_TASK_CONTEXTS_COUNT; i++) { - mActiveServices.add( - new TaskServiceContext(this, context.getMainLooper())); - } - mTasks = TaskStore.initAndGet(this); - } - - @Override - public void onStart() { - publishBinderService(Context.TASK_SERVICE, mTaskManagerStub); - } - - @Override - 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); - filter.addDataScheme("package"); - getContext().registerReceiverAsUser( - mBroadcastReceiver, UserHandle.ALL, filter, null, null); - final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); - getContext().registerReceiverAsUser( - mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); - } - } - - /** - * Called when we have a task status object that we need to insert in our - * {@link com.android.server.task.TaskStore}, and make sure all the relevant controllers know - * about. - */ - private void startTrackingTask(TaskStatus taskStatus) { - boolean update; - synchronized (mTasks) { - update = mTasks.add(taskStatus); - } - for (StateController controller : mControllers) { - if (update) { - controller.maybeStopTrackingTask(taskStatus); - } - controller.maybeStartTrackingTask(taskStatus); - } - } - - /** - * Called when we want to remove a TaskStatus object that we've finished executing. Returns the - * object removed. - */ - private boolean stopTrackingTask(TaskStatus taskStatus) { - boolean removed; - synchronized (mTasks) { - // Remove from store as well as controllers. - removed = mTasks.remove(taskStatus); - } - if (removed) { - for (StateController controller : mControllers) { - controller.maybeStopTrackingTask(taskStatus); - } - } - return removed; - } - - private boolean stopTaskOnServiceContextLocked(TaskStatus ts) { - for (TaskServiceContext tsc : mActiveServices) { - final TaskStatus executing = tsc.getRunningTask(); - if (executing != null && executing.matches(ts.getUid(), ts.getTaskId())) { - tsc.cancelExecutingTask(); - return true; - } - } - return false; - } - - /** - * @param ts TaskStatus we are querying against. - * @return Whether or not the task represented by the status object is currently being run or - * is pending. - */ - private boolean isCurrentlyActiveLocked(TaskStatus ts) { - for (TaskServiceContext serviceContext : mActiveServices) { - final TaskStatus running = serviceContext.getRunningTask(); - if (running != null && running.matches(ts.getUid(), ts.getTaskId())) { - return true; - } - } - return false; - } - - /** - * A task is rescheduled with exponential back-off if the client requests this from their - * execution logic. - * A caveat is for idle-mode tasks, for which the idle-mode constraint will usurp the - * timeliness of the reschedule. For an idle-mode task, no deadline is given. - * @param failureToReschedule Provided task status that we will reschedule. - * @return A newly instantiated TaskStatus with the same constraints as the last task except - * with adjusted timing constraints. - */ - private TaskStatus getRescheduleTaskForFailure(TaskStatus failureToReschedule) { - final long elapsedNowMillis = SystemClock.elapsedRealtime(); - final Task task = failureToReschedule.getTask(); - - final long initialBackoffMillis = task.getInitialBackoffMillis(); - final int backoffAttempt = failureToReschedule.getNumFailures() + 1; - long newEarliestRuntimeElapsed = elapsedNowMillis; - - switch (task.getBackoffPolicy()) { - case Task.BackoffPolicy.LINEAR: - newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt; - break; - default: - if (DEBUG) { - Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); - } - case Task.BackoffPolicy.EXPONENTIAL: - newEarliestRuntimeElapsed += - Math.pow(initialBackoffMillis * 0.001, backoffAttempt) * 1000; - break; - } - newEarliestRuntimeElapsed = - Math.min(newEarliestRuntimeElapsed, Task.MAX_BACKOFF_DELAY_MILLIS); - return new TaskStatus(failureToReschedule, newEarliestRuntimeElapsed, - TaskStatus.NO_LATEST_RUNTIME, backoffAttempt); - } - - /** - * Called after a periodic has executed so we can to re-add it. We take the last execution time - * of the task to be the time of completion (i.e. the time at which this function is called). - * This could be inaccurate b/c the task can run for as long as - * {@link com.android.server.task.TaskServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead - * to underscheduling at least, rather than if we had taken the last execution time to be the - * start of the execution. - * @return A new task representing the execution criteria for this instantiation of the - * recurring task. - */ - private TaskStatus getRescheduleTaskForPeriodic(TaskStatus periodicToReschedule) { - final long elapsedNow = SystemClock.elapsedRealtime(); - // Compute how much of the period is remaining. - long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0); - long newEarliestRunTimeElapsed = elapsedNow + runEarly; - long period = periodicToReschedule.getTask().getIntervalMillis(); - long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period; - - if (DEBUG) { - Slog.v(TAG, "Rescheduling executed periodic. New execution window [" + - newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s"); - } - return new TaskStatus(periodicToReschedule, newEarliestRunTimeElapsed, - newLatestRuntimeElapsed, 0 /* backoffAttempt */); - } - - // TaskCompletedListener implementations. - - /** - * A task just finished executing. We fetch the - * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on - * whether we want to reschedule we readd it to the controllers. - * @param taskStatus Completed task. - * @param needsReschedule Whether the implementing class should reschedule this task. - */ - @Override - public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule) { - if (DEBUG) { - Slog.d(TAG, "Completed " + taskStatus + ", reschedule=" + needsReschedule); - } - if (!stopTrackingTask(taskStatus)) { - if (DEBUG) { - Slog.e(TAG, "Error removing task: could not find task to remove. Was task " + - "removed while executing?"); - } - return; - } - if (needsReschedule) { - TaskStatus rescheduled = getRescheduleTaskForFailure(taskStatus); - startTrackingTask(rescheduled); - } else if (taskStatus.getTask().isPeriodic()) { - TaskStatus rescheduledPeriodic = getRescheduleTaskForPeriodic(taskStatus); - startTrackingTask(rescheduledPeriodic); - } - mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); - } - - // StateChangedListener implementations. - - /** - * Off-board work to our handler thread as quickly as possible, b/c this call is probably being - * made on the main thread. - * For now this takes the task and if it's ready to run it will run it. In future we might not - * provide the task, so that the StateChangedListener has to run through its list of tasks to - * see which are ready. This will further decouple the controllers from the execution logic. - */ - @Override - public void onControllerStateChanged() { - // Post a message to to run through the list of tasks and start/stop any that are eligible. - mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget(); - } - - @Override - public void onRunTaskNow(TaskStatus taskStatus) { - mHandler.obtainMessage(MSG_TASK_EXPIRED, taskStatus).sendToTarget(); - } - - /** - * Disk I/O is finished, take the list of tasks we read from disk and add them to our - * {@link TaskStore}. - * This is run on the {@link com.android.server.IoThread} instance, which is a separate thread, - * and is called once at boot. - */ - @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { - synchronized (mTasks) { - for (TaskStatus ts : tasks) { - if (mTasks.containsTaskIdForUid(ts.getTaskId(), ts.getUid())) { - // An app with BOOT_COMPLETED *might* have decided to reschedule their task, in - // the same amount of time it took us to read it from disk. If this is the case - // we leave it be. - continue; - } - startTrackingTask(ts); - } - } - } - - private class TaskHandler extends Handler { - - public TaskHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message message) { - switch (message.what) { - case MSG_TASK_EXPIRED: - synchronized (mTasks) { - TaskStatus runNow = (TaskStatus) message.obj; - if (!mPendingTasks.contains(runNow)) { - mPendingTasks.add(runNow); - } - } - queueReadyTasksForExecutionH(); - break; - case MSG_CHECK_TASKS: - // Check the list of tasks and run some of them if we feel inclined. - maybeQueueReadyTasksForExecutionH(); - break; - } - maybeRunPendingTasksH(); - // Don't remove TASK_EXPIRED in case one came along while processing the queue. - removeMessages(MSG_CHECK_TASKS); - } - - /** - * Run through list of tasks and execute all possible - at least one is expired so we do - * as many as we can. - */ - private void queueReadyTasksForExecutionH() { - synchronized (mTasks) { - for (TaskStatus ts : mTasks.getTasks()) { - if (isReadyToBeExecutedLocked(ts)) { - mPendingTasks.add(ts); - } else if (isReadyToBeCancelledLocked(ts)) { - stopTaskOnServiceContextLocked(ts); - } - } - } - } - - /** - * The state of at least one task has changed. Here is where we could enforce various - * policies on when we want to execute tasks. - * Right now the policy is such: - * If >1 of the ready tasks is idle mode we send all of them off - * if more than 2 network connectivity tasks are ready we send them all off. - * If more than 4 tasks total are ready we send them all off. - * TODO: It would be nice to consolidate these sort of high-level policies somewhere. - */ - private void maybeQueueReadyTasksForExecutionH() { - synchronized (mTasks) { - int idleCount = 0; - int backoffCount = 0; - int connectivityCount = 0; - List<TaskStatus> runnableTasks = new ArrayList<TaskStatus>(); - for (TaskStatus ts : mTasks.getTasks()) { - if (isReadyToBeExecutedLocked(ts)) { - if (ts.getNumFailures() > 0) { - backoffCount++; - } - if (ts.hasIdleConstraint()) { - idleCount++; - } - if (ts.hasConnectivityConstraint() || ts.hasUnmeteredConstraint()) { - connectivityCount++; - } - runnableTasks.add(ts); - } else if (isReadyToBeCancelledLocked(ts)) { - stopTaskOnServiceContextLocked(ts); - } - } - if (backoffCount > 0 || idleCount >= MIN_IDLE_COUNT || - connectivityCount >= MIN_CONNECTIVITY_COUNT || - runnableTasks.size() >= MIN_READY_TASKS_COUNT) { - for (TaskStatus ts : runnableTasks) { - mPendingTasks.add(ts); - } - } - } - } - - /** - * Criteria for moving a job into the pending queue: - * - It's ready. - * - It's not pending. - * - It's not already running on a TSC. - */ - private boolean isReadyToBeExecutedLocked(TaskStatus ts) { - return ts.isReady() && !mPendingTasks.contains(ts) && !isCurrentlyActiveLocked(ts); - } - - /** - * Criteria for cancelling an active job: - * - It's not ready - * - It's running on a TSC. - */ - private boolean isReadyToBeCancelledLocked(TaskStatus ts) { - return !ts.isReady() && isCurrentlyActiveLocked(ts); - } - - /** - * Reconcile jobs in the pending queue against available execution contexts. - * A controller can force a task into the pending queue even if it's already running, but - * here is where we decide whether to actually execute it. - */ - private void maybeRunPendingTasksH() { - synchronized (mTasks) { - Iterator<TaskStatus> it = mPendingTasks.iterator(); - while (it.hasNext()) { - TaskStatus nextPending = it.next(); - TaskServiceContext availableContext = null; - for (TaskServiceContext tsc : mActiveServices) { - final TaskStatus running = tsc.getRunningTask(); - if (running != null && running.matches(nextPending.getUid(), - nextPending.getTaskId())) { - // Already running this tId for this uId, skip. - availableContext = null; - break; - } - if (tsc.isAvailable()) { - availableContext = tsc; - } - } - if (availableContext != null) { - if (!availableContext.executeRunnableTask(nextPending)) { - if (DEBUG) { - Slog.d(TAG, "Error executing " + nextPending); - } - mTasks.remove(nextPending); - } - it.remove(); - } - } - } - } - } - - /** - * Binder stub trampoline implementation - */ - final class TaskManagerStub extends ITaskManager.Stub { - /** Cache determination of whether a given app can persist tasks - * key is uid of the calling app; value is undetermined/true/false - */ - private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>(); - - // Enforce that only the app itself (or shared uid participant) can schedule a - // task that runs one of the app's services, as well as verifying that the - // named service properly requires the BIND_TASK_SERVICE permission - private void enforceValidJobRequest(int uid, Task job) { - final PackageManager pm = getContext().getPackageManager(); - final ComponentName service = job.getService(); - try { - ServiceInfo si = pm.getServiceInfo(service, 0); - if (si.applicationInfo.uid != uid) { - throw new IllegalArgumentException("uid " + uid + - " cannot schedule job in " + service.getPackageName()); - } - if (!TaskService.PERMISSION_BIND.equals(si.permission)) { - throw new IllegalArgumentException("Scheduled service " + service - + " does not require android.permission.BIND_TASK_SERVICE permission"); - } - } catch (NameNotFoundException e) { - throw new IllegalArgumentException("No such service: " + service); - } - } - - private boolean canPersistJobs(int pid, int uid) { - // If we get this far we're good to go; all we need to do now is check - // whether the app is allowed to persist its scheduled work. - final boolean canPersist; - synchronized (mPersistCache) { - Boolean cached = mPersistCache.get(uid); - if (cached != null) { - canPersist = cached.booleanValue(); - } else { - // Persisting tasks is tantamount to running at boot, so we permit - // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED - // permission - int result = getContext().checkPermission( - android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid); - canPersist = (result == PackageManager.PERMISSION_GRANTED); - mPersistCache.put(uid, canPersist); - } - } - return canPersist; - } - - // ITaskManager implementation - @Override - public int schedule(Task task) throws RemoteException { - if (DEBUG) { - Slog.d(TAG, "Scheduling task: " + task); - } - final int pid = Binder.getCallingPid(); - final int uid = Binder.getCallingUid(); - - enforceValidJobRequest(uid, task); - final boolean canPersist = canPersistJobs(pid, uid); - - long ident = Binder.clearCallingIdentity(); - try { - return TaskManagerService.this.schedule(task, uid, canPersist); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public List<Task> getAllPendingTasks() throws RemoteException { - final int uid = Binder.getCallingUid(); - - long ident = Binder.clearCallingIdentity(); - try { - return TaskManagerService.this.getPendingTasks(uid); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void cancelAll() throws RemoteException { - final int uid = Binder.getCallingUid(); - - long ident = Binder.clearCallingIdentity(); - try { - TaskManagerService.this.cancelTasksForUid(uid); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void cancel(int taskId) throws RemoteException { - final int uid = Binder.getCallingUid(); - - long ident = Binder.clearCallingIdentity(); - try { - TaskManagerService.this.cancelTask(uid, taskId); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - /** - * "dumpsys" infrastructure - */ - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - long identityToken = Binder.clearCallingIdentity(); - try { - TaskManagerService.this.dumpInternal(pw); - } finally { - Binder.restoreCallingIdentity(identityToken); - } - } - }; - - void dumpInternal(PrintWriter pw) { - synchronized (mTasks) { - pw.println("Registered tasks:"); - if (mTasks.size() > 0) { - for (TaskStatus ts : mTasks.getTasks()) { - ts.dump(pw, " "); - } - } else { - pw.println(); - pw.println("No tasks scheduled."); - } - for (StateController controller : mControllers) { - pw.println(); - controller.dumpControllerState(pw); - } - pw.println(); - pw.println("Pending"); - for (TaskStatus taskStatus : mPendingTasks) { - pw.println(taskStatus.hashCode()); - } - pw.println(); - pw.println("Active jobs:"); - for (TaskServiceContext tsc : mActiveServices) { - if (tsc.isAvailable()) { - continue; - } else { - pw.println(tsc.getRunningTask().hashCode() + " for: " + - (SystemClock.elapsedRealtime() - - tsc.getExecutionStartTimeElapsed())/1000 + "s " + - "timeout: " + tsc.getTimeoutElapsed()); - } - } - } - pw.println(); - } -} diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 32546df..b9ef492 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -186,12 +186,16 @@ public class TrustManagerService extends SystemService { if (resolveInfo.serviceInfo == null) continue; String packageName = resolveInfo.serviceInfo.packageName; + // STOPSHIP Reenable this check once the GMS Core prebuild library has the + // permission. + /* if (pm.checkPermission(PERMISSION_PROVIDE_AGENT, packageName) != PackageManager.PERMISSION_GRANTED) { Log.w(TAG, "Skipping agent because package " + packageName + " does not have permission " + PERMISSION_PROVIDE_AGENT + "."); continue; } + */ ComponentName name = getComponentName(resolveInfo); if (!enabledAgents.contains(name)) continue; diff --git a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java index 55dd4ab..24318df 100644 --- a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java +++ b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java @@ -42,6 +42,7 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { private static final String seappContextsPath = "seapp_contexts"; private static final String versionPath = "selinux_version"; private static final String macPermissionsPath = "mac_permissions.xml"; + private static final String serviceContextsPath = "service_contexts"; public SELinuxPolicyInstallReceiver() { super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version"); @@ -65,6 +66,9 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { new File(contexts, sepolicyPath).renameTo( new File(contexts, sepolicyPath + "_backup")); + + new File(contexts, serviceContextsPath).renameTo( + new File(contexts, serviceContextsPath + "_backup")); } private void copyUpdate(File contexts) { @@ -74,6 +78,7 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath)); new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath)); new File(updateDir, sepolicyPath).renameTo(new File(contexts, sepolicyPath)); + new File(updateDir, serviceContextsPath).renameTo(new File(contexts, serviceContextsPath)); } private int readInt(BufferedInputStream reader) throws IOException { @@ -85,13 +90,14 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { } private int[] readChunkLengths(BufferedInputStream bundle) throws IOException { - int[] chunks = new int[6]; + int[] chunks = new int[7]; chunks[0] = readInt(bundle); chunks[1] = readInt(bundle); chunks[2] = readInt(bundle); chunks[3] = readInt(bundle); chunks[4] = readInt(bundle); chunks[5] = readInt(bundle); + chunks[6] = readInt(bundle); return chunks; } @@ -112,6 +118,7 @@ public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver { installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[3]); installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[4]); installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[5]); + installFile(new File(updateDir, serviceContextsPath), stream, chunkLengths[6]); } finally { IoUtils.closeQuietly(stream); } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index cfd09e5..6cb6b76 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -44,6 +44,7 @@ import android.view.MagnificationSpec; import android.view.Surface; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl; +import android.view.ViewConfiguration; import android.view.WindowInfo; import android.view.WindowManager; import android.view.WindowManagerInternal.MagnificationCallbacks; @@ -113,13 +114,13 @@ final class AccessibilityController { mDisplayMagnifier.setMagnificationSpecLocked(spec); } if (mWindowsForAccessibilityObserver != null) { - mWindowsForAccessibilityObserver.computeChangedWindows(); + mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } - public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) { + public void onRectangleOnScreenRequestedLocked(Rect rectangle) { if (mDisplayMagnifier != null) { - mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate); + mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); } // Not relevant for the window observer. } @@ -129,7 +130,7 @@ final class AccessibilityController { mDisplayMagnifier.onWindowLayersChangedLocked(); } if (mWindowsForAccessibilityObserver != null) { - mWindowsForAccessibilityObserver.computeChangedWindows(); + mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } @@ -138,7 +139,7 @@ final class AccessibilityController { mDisplayMagnifier.onRotationChangedLocked(displayContent, rotation); } if (mWindowsForAccessibilityObserver != null) { - mWindowsForAccessibilityObserver.computeChangedWindows(); + mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } @@ -154,7 +155,7 @@ final class AccessibilityController { mDisplayMagnifier.onWindowTransitionLocked(windowState, transition); } if (mWindowsForAccessibilityObserver != null) { - mWindowsForAccessibilityObserver.computeChangedWindows(); + mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } @@ -162,7 +163,16 @@ final class AccessibilityController { // Not relevant for the display magnifier. if (mWindowsForAccessibilityObserver != null) { - mWindowsForAccessibilityObserver.computeChangedWindows(); + mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); + } + } + + + public void onSomeWindowResizedOrMovedLocked() { + // Not relevant for the display magnifier. + + if (mWindowsForAccessibilityObserver != null) { + mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); } } @@ -247,7 +257,7 @@ final class AccessibilityController { mWindowManagerService.scheduleAnimationLocked(); } - public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) { + public void onRectangleOnScreenRequestedLocked(Rect rectangle) { if (DEBUG_RECTANGLE_REQUESTED) { Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); } @@ -880,21 +890,45 @@ final class AccessibilityController { private final WindowsForAccessibilityCallback mCallback; + private final long mRecurringAccessibilityEventsIntervalMillis; + public WindowsForAccessibilityObserver(WindowManagerService windowManagerService, WindowsForAccessibilityCallback callback) { mContext = windowManagerService.mContext; mWindowManagerService = windowManagerService; mCallback = callback; mHandler = new MyHandler(mWindowManagerService.mH.getLooper()); + mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration + .getSendRecurringAccessibilityEventsInterval(); computeChangedWindows(); } + public void scheduleComputeChangedWindowsLocked() { + // If focus changed, compute changed windows immediately as the focused window + // is used by the accessibility manager service to determine the active window. + if (mWindowManagerService.mCurrentFocus != null + && mWindowManagerService.mCurrentFocus != mWindowManagerService.mLastFocus) { + mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS); + computeChangedWindows(); + } else if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) { + mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS, + mRecurringAccessibilityEventsIntervalMillis); + } + } + public void computeChangedWindows() { if (DEBUG) { Slog.i(LOG_TAG, "computeChangedWindows()"); } synchronized (mWindowManagerService.mWindowMap) { + // Do not send the windows if there is no current focus as + // the window manager is still looking for where to put it. + // We will do the work when we get a focus change callback. + if (mWindowManagerService.mCurrentFocus == null) { + return; + } + WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); windowManager.getDefaultDisplay().getRealSize(mTempPoint); @@ -912,37 +946,21 @@ final class AccessibilityController { Set<IBinder> addedWindows = mTempBinderSet; addedWindows.clear(); + boolean focusedWindowAdded = false; + final int visibleWindowCount = visibleWindows.size(); for (int i = visibleWindowCount - 1; i >= 0; i--) { WindowState windowState = visibleWindows.valueAt(i); - // Compute the window touchable frame as shown on the screen. - - // Get the touchable frame. - Region touchableRegion = mTempRegion1; - windowState.getTouchableRegion(touchableRegion); - Rect touchableFrame = mTempRect; - touchableRegion.getBounds(touchableFrame); - - // Move to origin as all transforms are captured by the matrix. - RectF windowFrame = mTempRectF; - windowFrame.set(touchableFrame); - windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top); - - // Map the frame to get what appears on the screen. - Matrix matrix = mTempMatrix; - populateTransformationMatrixLocked(windowState, matrix); - matrix.mapRect(windowFrame); - // Got the bounds. + // Compute the bounds in the screen. Rect boundsInScreen = mTempRect; - boundsInScreen.set((int) windowFrame.left, (int) windowFrame.top, - (int) windowFrame.right, (int) windowFrame.bottom); + computeWindowBoundsInScreen(windowState, boundsInScreen); final int flags = windowState.mAttrs.flags; // If the window is not touchable, do not report it but take into account // the space it takes since the content behind it cannot be touched. - if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 1) { + if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { unaccountedSpace.op(boundsInScreen, unaccountedSpace, Region.Op.DIFFERENCE); continue; @@ -956,33 +974,12 @@ final class AccessibilityController { // Add windows of certain types not covered by modal windows. if (isReportedWindowType(windowState.mAttrs.type)) { // Add the window to the ones to be reported. - WindowInfo window = WindowInfo.obtain(); - window.type = windowState.mAttrs.type; - window.layer = windowState.mLayer; - window.token = windowState.mClient.asBinder(); - + WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen); addedWindows.add(window.token); - - WindowState attachedWindow = windowState.mAttachedWindow; - if (attachedWindow != null) { - window.parentToken = attachedWindow.mClient.asBinder(); - } - - window.focused = windowState.isFocused(); - window.boundsInScreen.set(boundsInScreen); - - final int childCount = windowState.mChildWindows.size(); - if (childCount > 0) { - if (window.childTokens == null) { - window.childTokens = new ArrayList<IBinder>(); - } - for (int j = 0; j < childCount; j++) { - WindowState child = windowState.mChildWindows.get(j); - window.childTokens.add(child.mClient.asBinder()); - } - } - windows.add(window); + if (windowState.isFocused()) { + focusedWindowAdded = true; + } } // Account for the space this window takes. @@ -1001,6 +998,25 @@ final class AccessibilityController { } } + // Always report the focused window. + if (!focusedWindowAdded) { + for (int i = visibleWindowCount - 1; i >= 0; i--) { + WindowState windowState = visibleWindows.valueAt(i); + if (windowState.isFocused()) { + // Compute the bounds in the screen. + Rect boundsInScreen = mTempRect; + computeWindowBoundsInScreen(windowState, boundsInScreen); + + // Add the window to the ones to be reported. + WindowInfo window = obtainPopulatedWindowInfo(windowState, + boundsInScreen); + addedWindows.add(window.token); + windows.add(window); + break; + } + } + } + // Remove child/parent references to windows that were not added. final int windowCount = windows.size(); for (int i = 0; i < windowCount; i++) { @@ -1063,6 +1079,57 @@ final class AccessibilityController { } } + private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) { + // Get the touchable frame. + Region touchableRegion = mTempRegion1; + windowState.getTouchableRegion(touchableRegion); + Rect touchableFrame = mTempRect; + touchableRegion.getBounds(touchableFrame); + + // Move to origin as all transforms are captured by the matrix. + RectF windowFrame = mTempRectF; + windowFrame.set(touchableFrame); + windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top); + + // Map the frame to get what appears on the screen. + Matrix matrix = mTempMatrix; + populateTransformationMatrixLocked(windowState, matrix); + matrix.mapRect(windowFrame); + + // Got the bounds. + outBounds.set((int) windowFrame.left, (int) windowFrame.top, + (int) windowFrame.right, (int) windowFrame.bottom); + } + + private static WindowInfo obtainPopulatedWindowInfo(WindowState windowState, + Rect boundsInScreen) { + WindowInfo window = WindowInfo.obtain(); + window.type = windowState.mAttrs.type; + window.layer = windowState.mLayer; + window.token = windowState.mClient.asBinder(); + + WindowState attachedWindow = windowState.mAttachedWindow; + if (attachedWindow != null) { + window.parentToken = attachedWindow.mClient.asBinder(); + } + + window.focused = windowState.isFocused(); + window.boundsInScreen.set(boundsInScreen); + + final int childCount = windowState.mChildWindows.size(); + if (childCount > 0) { + if (window.childTokens == null) { + window.childTokens = new ArrayList<IBinder>(); + } + for (int j = 0; j < childCount; j++) { + WindowState child = windowState.mChildWindows.get(j); + window.childTokens.add(child.mClient.asBinder()); + } + } + + return window; + } + private void cacheWindows(List<WindowInfo> windows) { final int oldWindowCount = mOldWindows.size(); for (int i = oldWindowCount - 1; i >= 0; i--) { @@ -1079,10 +1146,10 @@ final class AccessibilityController { if (oldWindow == newWindow) { return false; } - if (oldWindow == null && newWindow != null) { + if (oldWindow == null) { return true; } - if (oldWindow != null && newWindow == null) { + if (newWindow == null) { return true; } if (oldWindow.type != newWindow.type) { @@ -1151,7 +1218,8 @@ final class AccessibilityController { } private class MyHandler extends Handler { - public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1; + public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1; + public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 2; public MyHandler(Looper looper) { super(looper, null, false); @@ -1161,6 +1229,10 @@ final class AccessibilityController { @SuppressWarnings("unchecked") public void handleMessage(Message message) { switch (message.what) { + case MESSAGE_COMPUTE_CHANGED_WINDOWS: { + computeChangedWindows(); + } break; + case MESSAGE_NOTIFY_WINDOWS_CHANGED: { List<WindowInfo> windows = (List<WindowInfo>) message.obj; mCallback.onWindowsForAccessibilityChanged(windows); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 3200b54..b4cf2ae 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -446,11 +446,11 @@ final class Session extends IWindowSession.Stub } } - public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) { + public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) { synchronized(mService.mWindowMap) { final long identity = Binder.clearCallingIdentity(); try { - mService.onRectangleOnScreenRequested(token, rectangle, immediate); + mService.onRectangleOnScreenRequested(token, rectangle); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 008d2fc..45326f7 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED; @@ -227,7 +228,7 @@ public class WindowAnimator { continue; } final WindowStateAnimator winAnimator = win.mWinAnimator; - if (mPolicy.doesForceHide(win.mAttrs)) { + if ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { if (!winAnimator.mAnimating) { // Create a new animation to delay until keyguard is gone on its own. winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f); @@ -268,7 +269,7 @@ public class WindowAnimator { } } - if (mPolicy.doesForceHide(win.mAttrs)) { + if (mPolicy.isForceHiding(win.mAttrs)) { if (!wasAnimating && nowAnimating) { if (WindowManagerService.DEBUG_ANIM || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2a5dbb4..4eea9c3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -151,7 +151,6 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -1484,10 +1483,15 @@ public class WindowManagerService extends IWindowManager.Stub if (pos >= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; - if (pos < windows.size()) { - WindowState wp = windows.get(pos); - if (wp == mInputMethodWindow) { - pos++; + // Skip windows owned by the input method. + if (mInputMethodWindow != null) { + while (pos < windows.size()) { + WindowState wp = windows.get(pos); + if (wp == mInputMethodWindow || wp.mAttachedWindow == mInputMethodWindow) { + pos++; + continue; + } + break; } } if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Adding " + N + " dialogs at pos=" + pos); @@ -2807,14 +2811,13 @@ public class WindowManagerService extends IWindowManager.Stub performLayoutAndPlaceSurfacesLocked(); } - public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) { + public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) { synchronized (mWindowMap) { if (mAccessibilityController != null) { WindowState window = mWindowMap.get(token); //TODO (multidisplay): Magnification is supported only for the default display. if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { - mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle, - immediate); + mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle); } } } @@ -8439,7 +8442,7 @@ public class WindowManagerService extends IWindowManager.Stub } win.mLayoutNeeded = false; win.prelayout(); - mPolicy.layoutWindowLw(win, win.mAttrs, null); + mPolicy.layoutWindowLw(win, null); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + win.mFrame + " mContainingFrame=" @@ -8491,7 +8494,7 @@ public class WindowManagerService extends IWindowManager.Stub } win.mLayoutNeeded = false; win.prelayout(); - mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + mPolicy.layoutWindowLw(win, win.mAttachedWindow); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + win.mFrame + " mContainingFrame=" @@ -9223,6 +9226,13 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.setAnimation(a); winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left; winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top; + + //TODO (multidisplay): Accessibility supported only for the default display. + if (mAccessibilityController != null + && displayId == Display.DEFAULT_DISPLAY) { + mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + } + try { w.mClient.moved(w.mFrame.left, w.mFrame.top); } catch (RemoteException e) { @@ -9861,7 +9871,9 @@ public class WindowManagerService extends IWindowManager.Stub mCurrentFocus = newFocus; mLosingFocus.remove(newFocus); - if (mAccessibilityController != null) { + // TODO(multidisplay): Accessibilty supported only of default desiplay. + if (mAccessibilityController != null + && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onWindowFocusChangedLocked(); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4a80e3e..97178bd 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -36,6 +36,7 @@ import android.os.Debug; import android.os.RemoteCallbackList; import android.os.SystemClock; import android.util.TimeUtils; +import android.view.Display; import android.view.IWindowFocusObserver; import android.view.IWindowId; import com.android.server.input.InputWindowHandle; @@ -1359,6 +1360,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, reportDraw, newConfig); } + + //TODO (multidisplay): Accessibility supported only for the default display. + if (mService.mAccessibilityController != null + && getDisplayId() == Display.DEFAULT_DISPLAY) { + mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + } + mOverscanInsetsChanged = false; mContentInsetsChanged = false; mVisibleInsetsChanged = false; diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp index a734026..df3b6d3 100644 --- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp +++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp @@ -57,12 +57,21 @@ public: int getVersion(); // Get vendor id used for vendor command. uint32_t getVendorId(); + // Get Port information on all the HDMI ports. + jobjectArray getPortInfos(); + // Set a flag and its value. + void setOption(int flag, int value); + // Set audio return channel status. + void setAudioReturnChannel(bool flag); + // Whether to hdmi device is connected to the given port. + bool isConnected(int port); jobject getCallbacksObj() const { return mCallbacksObj; } private: + static const int INVALID_PHYSICAL_ADDRESS = 0xFFFF; static void onReceived(const hdmi_event_t* event, void* arg); hdmi_cec_device_t* mDevice; @@ -203,11 +212,11 @@ void HdmiCecController::clearLogicaladdress() { } int HdmiCecController::getPhysicalAddress() { - uint16_t physicalAddress = 0xFFFF; - if (mDevice->get_physical_address(mDevice, &physicalAddress) == 0) { - return physicalAddress; + uint16_t addr; + if (!mDevice->get_physical_address(mDevice, &addr)) { + return addr; } - return -1; + return INVALID_PHYSICAL_ADDRESS; } int HdmiCecController::getVersion() { @@ -222,6 +231,47 @@ uint32_t HdmiCecController::getVendorId() { return vendorId; } +jobjectArray HdmiCecController::getPortInfos() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jclass hdmiPortInfo = env->FindClass("com/android/server/hdmi/HdmiPortInfo"); + if (hdmiPortInfo == NULL) { + return NULL; + } + jmethodID ctor = env->GetMethodID(hdmiPortInfo, "<init>", "(IIIZZZ)V"); + if (ctor == NULL) { + return NULL; + } + hdmi_port_info* ports; + int numPorts; + mDevice->get_port_info(mDevice, &ports, &numPorts); + jobjectArray res = env->NewObjectArray(numPorts, hdmiPortInfo, NULL); + + // MHL support field will be obtained from MHL HAL. Leave it to false. + jboolean mhlSupported = (jboolean) 0; + for (int i = 0; i < numPorts; ++i) { + hdmi_port_info* info = &ports[i]; + jboolean cecSupported = (jboolean) info->cec_supported; + jboolean arcSupported = (jboolean) info->arc_supported; + jobject infoObj = env->NewObject(hdmiPortInfo, ctor, info->port_num, info->type, + info->physical_address, cecSupported, mhlSupported, arcSupported); + env->SetObjectArrayElement(res, i, infoObj); + } + return res; +} + +void HdmiCecController::setOption(int flag, int value) { + mDevice->set_option(mDevice, flag, value); +} + +// Set audio return channel status. +void HdmiCecController::setAudioReturnChannel(bool enabled) { + mDevice->set_audio_return_channel(mDevice, enabled ? 1 : 0); +} + +// Whether to hdmi device is connected to the given port. +bool HdmiCecController::isConnected(int port) { + return mDevice->is_connected(mDevice, port) == HDMI_CONNECTED; +} // static void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) { @@ -291,41 +341,53 @@ static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr, return controller->sendMessage(message); } -static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, - jlong controllerPtr, jint logicalAddress) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); - return controller->addLogicalAddress( - static_cast<cec_logical_address_t>(logicalAddress)); +static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr, + jint logicalAddress) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->addLogicalAddress(static_cast<cec_logical_address_t>(logicalAddress)); } -static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, - jlong controllerPtr) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); +static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); controller->clearLogicaladdress(); } -static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, - jlong controllerPtr) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); +static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); return controller->getPhysicalAddress(); } -static jint nativeGetVersion(JNIEnv* env, jclass clazz, - jlong controllerPtr) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); +static jint nativeGetVersion(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); return controller->getVersion(); } static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) { - HdmiCecController* controller = - reinterpret_cast<HdmiCecController*>(controllerPtr); + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); return controller->getVendorId(); } +static jobjectArray nativeGetPortInfos(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->getPortInfos(); +} + +static void nativeSetOption(JNIEnv* env, jclass clazz, jlong controllerPtr, jint flag, jint value) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); + controller->setOption(flag, value); +} + +static void nativeSetAudioReturnChannel(JNIEnv* env, jclass clazz, jlong controllerPtr, + jboolean enabled) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); + controller->setAudioReturnChannel(enabled == JNI_TRUE); +} + +static jboolean nativeIsConnected(JNIEnv* env, jclass clazz, jlong controllerPtr, jint port) { + HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->isConnected(port) ? JNI_TRUE : JNI_FALSE ; +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ { "nativeInit", @@ -337,13 +399,18 @@ static JNINativeMethod sMethods[] = { { "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress }, { "nativeGetVersion", "(J)I", (void *) nativeGetVersion }, { "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId }, + { "nativeGetPortInfos", + "(J)[Lcom/android/server/hdmi/HdmiPortInfo;", + (void *) nativeGetPortInfos }, + { "nativeSetOption", "(JII)V", (void *) nativeSetOption }, + { "nativeSetAudioReturnChannel", "(JZ)V", (void *) nativeSetAudioReturnChannel }, + { "nativeIsConnected", "(JI)Z", (void *) nativeIsConnected }, }; #define CLASS_PATH "com/android/server/hdmi/HdmiCecController" int register_android_server_hdmi_HdmiCecController(JNIEnv* env) { - int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, - NELEM(sMethods)); + int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); return 0; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5cfe0f1..4574caf 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -32,6 +32,7 @@ import android.app.Activity; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.AppGlobals; +import android.app.IActivityManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -818,12 +819,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (who != null) { + if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) { + throw new SecurityException("Admin " + candidates.get(0).info.getComponent() + + " does not own the device"); + } + if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) { + throw new SecurityException("Admin " + candidates.get(0).info.getComponent() + + " does not own the profile"); + } throw new SecurityException("Admin " + candidates.get(0).info.getComponent() + " did not specify uses-policy for: " + candidates.get(0).info.getTagForPolicy(reqPolicy)); } else { throw new SecurityException("No active admin owned by uid " - + Binder.getCallingUid() + " for policy:" + reqPolicy); + + Binder.getCallingUid() + " for policy #" + reqPolicy); } } @@ -831,15 +840,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { sendAdminCommandLocked(admin, action, null); } + void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) { + sendAdminCommandLocked(admin, action, null, result); + } + /** * Send an update to one specific admin, get notified when that admin returns a result. */ - void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) { + void sendAdminCommandLocked(ActiveAdmin admin, String action, Bundle adminExtras, + BroadcastReceiver result) { Intent intent = new Intent(action); intent.setComponent(admin.info.getComponent()); if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) { intent.putExtra("expiration", admin.passwordExpirationDate); } + if (adminExtras != null) { + intent.putExtras(adminExtras); + } if (result != null) { mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(), null, result, mHandler, Activity.RESULT_OK, null, null); @@ -1367,6 +1384,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mHasFeature) { return; } + setActiveAdmin(adminReceiver, refreshing, userHandle, null); + } + + private void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle, + Bundle onEnableData) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.MANAGE_DEVICE_ADMINS, null); enforceCrossUserPermission(userHandle); @@ -1403,7 +1425,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.mAdminList.set(replaceIndex, newAdmin); } saveSettingsLocked(userHandle); - sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED); + sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED, + onEnableData, null); } finally { Binder.restoreCallingIdentity(ident); } @@ -3490,6 +3513,36 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public UserHandle createAndInitializeUser(ComponentName who, String name, + String ownerName, ComponentName profileOwnerComponent, Bundle adminExtras) { + UserHandle user = createUser(who, name); + long id = Binder.clearCallingIdentity(); + try { + String profileOwnerPkg = profileOwnerComponent.getPackageName(); + final IPackageManager ipm = AppGlobals.getPackageManager(); + IActivityManager activityManager = ActivityManagerNative.getDefault(); + + try { + // Install the profile owner if not present. + if (!ipm.isPackageAvailable(profileOwnerPkg, user.getIdentifier())) { + ipm.installExistingPackageAsUser(profileOwnerPkg, user.getIdentifier()); + } + + // Start user in background. + activityManager.startUserInBackground(user.getIdentifier()); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Failed to make remote calls for configureUser", e); + } + + setActiveAdmin(profileOwnerComponent, true, user.getIdentifier(), adminExtras); + setProfileOwner(profileOwnerPkg, ownerName, user.getIdentifier()); + return user; + } finally { + restoreCallingIdentity(id); + } + } + + @Override public boolean removeUser(ComponentName who, UserHandle userHandle) { synchronized (this) { if (who == null) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index bb61b81..3102cce 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -65,6 +65,7 @@ import com.android.server.display.DisplayManagerService; import com.android.server.dreams.DreamManagerService; import com.android.server.hdmi.HdmiControlService; import com.android.server.input.InputManagerService; +import com.android.server.job.JobSchedulerService; import com.android.server.lights.LightsManager; import com.android.server.lights.LightsService; import com.android.server.media.MediaRouterService; @@ -84,7 +85,6 @@ import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.search.SearchManagerService; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.storage.DeviceStorageMonitorService; -import com.android.server.task.TaskManagerService; import com.android.server.trust.TrustManagerService; import com.android.server.tv.TvInputManagerService; import com.android.server.twilight.TwilightService; @@ -132,8 +132,8 @@ public final class SystemServer { "com.android.server.wifi.p2p.WifiP2pService"; private static final String ETHERNET_SERVICE_CLASS = "com.android.server.ethernet.EthernetService"; - private static final String TASK_SERVICE_CLASS = - "com.android.server.task.TaskManagerService"; + private static final String JOB_SCHEDULER_SERVICE_CLASS = + "com.android.server.job.JobSchedulerService"; private final int mFactoryTestMode; private Timer mProfilerSnapshotTimer; @@ -349,6 +349,7 @@ public final class SystemServer { boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false); boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false); boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false); + boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); try { Slog.i(TAG, "Telephony Registry"); @@ -461,7 +462,7 @@ public final class SystemServer { // Skip Bluetooth if we have an emulator kernel // TODO: Use a more reliable check to see if this product should // support Bluetooth - see bug 988521 - if (SystemProperties.get("ro.kernel.qemu").equals("1")) { + if (isEmulator) { Slog.i(TAG, "No Bluetooh Service (emulator)"); } else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) { Slog.i(TAG, "No Bluetooth Service (factory test)"); @@ -662,10 +663,17 @@ public final class SystemServer { reportWtf("starting Wi-Fi Scanning Service", e); } - try { - mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS); - } catch (Throwable e) { - reportWtf("starting Ethernet Service", e); + if (!isEmulator) { + try { + mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS); + } catch (Throwable e) { + reportWtf("starting Ethernet Service", e); + } + } else { + // Don't start the Ethernet service on the emulator because + // it interferes with qemu's SLIRP emulation, which uses + // IPv4 over eth0. http://b/15341003 . + Slog.i(TAG, "Not starting Ethernet service (emulator)"); } try { @@ -826,7 +834,7 @@ public final class SystemServer { mSystemServiceManager.startService(UiModeManagerService.class); - mSystemServiceManager.startService(TaskManagerService.class); + mSystemServiceManager.startService(JobSchedulerService.class); if (!disableNonCoreServices) { try { diff --git a/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java b/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java index e7f9ca0..7a7fa07 100644 --- a/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java @@ -3,18 +3,19 @@ package com.android.server.task; import android.content.ComponentName; import android.content.Context; -import android.app.task.Task; -import android.app.task.Task.Builder; +import android.app.job.JobInfo; +import android.app.job.JobInfo.Builder; import android.os.PersistableBundle; import android.test.AndroidTestCase; import android.test.RenamingDelegatingContext; import android.util.Log; -import com.android.server.task.controllers.TaskStatus; +import com.android.server.job.JobMapReadFinishedListener; +import com.android.server.job.JobStore; +import com.android.server.job.controllers.JobStatus; import java.util.List; -import static com.android.server.task.TaskStore.initAndGet; /** * Test reading and writing correctly from file. */ @@ -26,12 +27,12 @@ public class TaskStoreTest extends AndroidTestCase { private ComponentName mComponent; private static final long IO_WAIT = 600L; - TaskStore mTaskStoreUnderTest; + JobStore mTaskStoreUnderTest; Context mTestContext; - TaskMapReadFinishedListener mTaskMapReadFinishedListenerStub = - new TaskMapReadFinishedListener() { + JobMapReadFinishedListener mTaskMapReadFinishedListenerStub = + new JobMapReadFinishedListener() { @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { + public void onJobMapReadFinished(List<JobStatus> tasks) { // do nothing. } }; @@ -40,7 +41,7 @@ public class TaskStoreTest extends AndroidTestCase { public void setUp() throws Exception { mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX); Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'"); - mTaskStoreUnderTest = TaskStore.initAndGetForTesting(mTestContext, + mTaskStoreUnderTest = JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir(), mTaskMapReadFinishedListenerStub); mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName()); } @@ -56,23 +57,23 @@ public class TaskStoreTest extends AndroidTestCase { long runFromMillis = 2000L; // 2s long initialBackoff = 10000L; // 10s - final Task task = new Builder(taskId, mComponent) + final JobInfo task = new Builder(taskId, mComponent) .setRequiresCharging(true) - .setRequiredNetworkCapabilities(Task.NetworkType.ANY) - .setBackoffCriteria(initialBackoff, Task.BackoffPolicy.EXPONENTIAL) + .setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY) + .setBackoffCriteria(initialBackoff, JobInfo.BackoffPolicy.EXPONENTIAL) .setOverrideDeadline(runByMillis) .setMinimumLatency(runFromMillis) .build(); - final TaskStatus ts = new TaskStatus(task, SOME_UID, true /* persisted */); + final JobStatus ts = new JobStatus(task, SOME_UID, true /* persisted */); mTaskStoreUnderTest.add(ts); Thread.sleep(IO_WAIT); // Manually load tasks from xml file. - mTaskStoreUnderTest.readTaskMapFromDisk(new TaskMapReadFinishedListener() { + mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() { @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { + public void onJobMapReadFinished(List<JobStatus> tasks) { assertEquals("Didn't get expected number of persisted tasks.", 1, tasks.size()); - TaskStatus loadedTaskStatus = tasks.get(0); - assertTasksEqual(task, loadedTaskStatus.getTask()); + JobStatus loadedTaskStatus = tasks.get(0); + assertTasksEqual(task, loadedTaskStatus.getJob()); assertEquals("Different uids.", SOME_UID, tasks.get(0).getUid()); compareTimestampsSubjectToIoLatency("Early run-times not the same after read.", ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime()); @@ -84,30 +85,30 @@ public class TaskStoreTest extends AndroidTestCase { } public void testWritingTwoFilesToDisk() throws Exception { - final Task task1 = new Builder(8, mComponent) + final JobInfo task1 = new Builder(8, mComponent) .setRequiresDeviceIdle(true) .setPeriodic(10000L) .setRequiresCharging(true) .build(); - final Task task2 = new Builder(12, mComponent) + final JobInfo task2 = new Builder(12, mComponent) .setMinimumLatency(5000L) - .setBackoffCriteria(15000L, Task.BackoffPolicy.LINEAR) + .setBackoffCriteria(15000L, JobInfo.BackoffPolicy.LINEAR) .setOverrideDeadline(30000L) - .setRequiredNetworkCapabilities(Task.NetworkType.UNMETERED) + .setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED) .build(); - final TaskStatus taskStatus1 = new TaskStatus(task1, SOME_UID, true /* persisted */); - final TaskStatus taskStatus2 = new TaskStatus(task2, SOME_UID, true /* persisted */); + final JobStatus taskStatus1 = new JobStatus(task1, SOME_UID, true /* persisted */); + final JobStatus taskStatus2 = new JobStatus(task2, SOME_UID, true /* persisted */); mTaskStoreUnderTest.add(taskStatus1); mTaskStoreUnderTest.add(taskStatus2); Thread.sleep(IO_WAIT); - mTaskStoreUnderTest.readTaskMapFromDisk(new TaskMapReadFinishedListener() { + mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() { @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { + public void onJobMapReadFinished(List<JobStatus> tasks) { assertEquals("Incorrect # of persisted tasks.", 2, tasks.size()); - TaskStatus loaded1 = tasks.get(0); - TaskStatus loaded2 = tasks.get(1); - assertTasksEqual(task1, loaded1.getTask()); - assertTasksEqual(task2, loaded2.getTask()); + JobStatus loaded1 = tasks.get(0); + JobStatus loaded2 = tasks.get(1); + assertTasksEqual(task1, loaded1.getJob()); + assertTasksEqual(task2, loaded2.getJob()); // Check that the loaded task has the correct runtimes. compareTimestampsSubjectToIoLatency("Early run-times not the same after read.", @@ -124,7 +125,7 @@ public class TaskStoreTest extends AndroidTestCase { } public void testWritingTaskWithExtras() throws Exception { - Task.Builder b = new Builder(8, mComponent) + JobInfo.Builder b = new Builder(8, mComponent) .setRequiresDeviceIdle(true) .setPeriodic(10000L) .setRequiresCharging(true); @@ -134,17 +135,17 @@ public class TaskStoreTest extends AndroidTestCase { extras.putString("hi", "there"); extras.putInt("into", 3); b.setExtras(extras); - final Task task = b.build(); - TaskStatus taskStatus = new TaskStatus(task, SOME_UID, true /* persisted */); + final JobInfo task = b.build(); + JobStatus taskStatus = new JobStatus(task, SOME_UID, true /* persisted */); mTaskStoreUnderTest.add(taskStatus); Thread.sleep(IO_WAIT); - mTaskStoreUnderTest.readTaskMapFromDisk(new TaskMapReadFinishedListener() { + mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() { @Override - public void onTaskMapReadFinished(List<TaskStatus> tasks) { + public void onJobMapReadFinished(List<JobStatus> tasks) { assertEquals("Incorrect # of persisted tasks.", 1, tasks.size()); - TaskStatus loaded = tasks.get(0); - assertTasksEqual(task, loaded.getTask()); + JobStatus loaded = tasks.get(0); + assertTasksEqual(task, loaded.getJob()); } }); @@ -153,7 +154,7 @@ public class TaskStoreTest extends AndroidTestCase { /** * Helper function to throw an error if the provided task and TaskStatus objects are not equal. */ - private void assertTasksEqual(Task first, Task second) { + private void assertTasksEqual(JobInfo first, JobInfo second) { assertEquals("Different task ids.", first.getId(), second.getId()); assertEquals("Different components.", first.getService(), second.getService()); assertEquals("Different periodic status.", first.isPeriodic(), second.isPeriodic()); @@ -168,11 +169,11 @@ public class TaskStoreTest extends AndroidTestCase { assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(), second.isRequireDeviceIdle()); assertEquals("Invalid unmetered constraint.", - first.getNetworkCapabilities() == Task.NetworkType.UNMETERED, - second.getNetworkCapabilities() == Task.NetworkType.UNMETERED); + first.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED, + second.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED); assertEquals("Invalid connectivity constraint.", - first.getNetworkCapabilities() == Task.NetworkType.ANY, - second.getNetworkCapabilities() == Task.NetworkType.ANY); + first.getNetworkCapabilities() == JobInfo.NetworkType.ANY, + second.getNetworkCapabilities() == JobInfo.NetworkType.ANY); assertEquals("Invalid deadline constraint.", first.hasLateConstraint(), second.hasLateConstraint()); diff --git a/services/tests/servicestests/src/com/android/server/task/controllers/BatteryControllerTest.java b/services/tests/servicestests/src/com/android/server/task/controllers/BatteryControllerTest.java deleted file mode 100644 index 6617a05..0000000 --- a/services/tests/servicestests/src/com/android/server/task/controllers/BatteryControllerTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2014 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.task.controllers; - - -import android.content.ComponentName; -import android.content.Intent; -import android.test.AndroidTestCase; - -import com.android.server.task.StateChangedListener; - -import static com.android.server.task.controllers.BatteryController.getForTesting; - -import static org.mockito.Mockito.*; - -/** - * - */ -public class BatteryControllerTest extends AndroidTestCase { - BatteryController mBatteryControllerUnderTest; - - StateChangedListener mStateChangedListenerStub = new StateChangedListener() { - @Override - public void onControllerStateChanged() { - - } - - @Override - public void onRunTaskNow(TaskStatus taskStatus) { - - } - }; - BatteryController.ChargingTracker mTrackerUnderTest; - - public void setUp() throws Exception { - mBatteryControllerUnderTest = getForTesting(mStateChangedListenerStub, getTestContext()); - mTrackerUnderTest = mBatteryControllerUnderTest.getTracker(); - } - - public void testSendBatteryChargingIntent() throws Exception { - Intent batteryConnectedIntent = new Intent(Intent.ACTION_POWER_CONNECTED) - .setComponent(new ComponentName(getContext(), mTrackerUnderTest.getClass())); - Intent batteryHealthyIntent = new Intent(Intent.ACTION_BATTERY_OKAY) - .setComponent(new ComponentName(getContext(), mTrackerUnderTest.getClass())); - - mTrackerUnderTest.onReceiveInternal(batteryConnectedIntent); - mTrackerUnderTest.onReceiveInternal(batteryHealthyIntent); - - assertTrue(mTrackerUnderTest.isOnStablePower()); - } - -}
\ No newline at end of file diff --git a/telecomm/java/android/telecomm/CallCapabilities.java b/telecomm/java/android/telecomm/CallCapabilities.java index b2b33a3..5aff19c 100644 --- a/telecomm/java/android/telecomm/CallCapabilities.java +++ b/telecomm/java/android/telecomm/CallCapabilities.java @@ -17,7 +17,7 @@ package android.telecomm; /** Defines actions a call currently supports. */ -public class CallCapabilities { +public final class CallCapabilities { /** Call can currently be put on hold or unheld. */ public static final int HOLD = 0x00000001; @@ -27,24 +27,60 @@ public class CallCapabilities { /** Call can currently be merged. */ public static final int MERGE_CALLS = 0x00000004; - /* Call can currently be swapped with another call. */ + /** Call can currently be swapped with another call. */ public static final int SWAP_CALLS = 0x00000008; - /* Call currently supports adding another call to this one. */ + /** Call currently supports adding another call to this one. */ public static final int ADD_CALL = 0x00000010; - /* Call supports responding via text option. */ + /** Call supports responding via text option. */ public static final int RESPOND_VIA_TEXT = 0x00000020; - /* Call can be muted. */ + /** Call can be muted. */ public static final int MUTE = 0x00000040; - /* Call supports generic conference mode. */ + /** Call supports generic conference mode. */ public static final int GENERIC_CONFERENCE = 0x00000080; - /* Call currently supports switch between connections. */ + /** Call currently supports switch between connections. */ public static final int CONNECTION_HANDOFF = 0x00000100; public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL | RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE | CONNECTION_HANDOFF; + + public static String toString(int capabilities) { + StringBuilder builder = new StringBuilder(); + builder.append("[Capabilities:"); + if ((capabilities & HOLD) != 0) { + builder.append(" HOLD"); + } + if ((capabilities & SUPPORT_HOLD) != 0) { + builder.append(" SUPPORT_HOLD"); + } + if ((capabilities & MERGE_CALLS) != 0) { + builder.append(" MERGE_CALLS"); + } + if ((capabilities & SWAP_CALLS) != 0) { + builder.append(" SWAP_CALLS"); + } + if ((capabilities & ADD_CALL) != 0) { + builder.append(" ADD_CALL"); + } + if ((capabilities & RESPOND_VIA_TEXT) != 0) { + builder.append(" RESPOND_VIA_TEXT"); + } + if ((capabilities & MUTE) != 0) { + builder.append(" MUTE"); + } + if ((capabilities & GENERIC_CONFERENCE) != 0) { + builder.append(" GENERIC_CONFERENCE"); + } + if ((capabilities & CONNECTION_HANDOFF) != 0) { + builder.append(" HANDOFF"); + } + builder.append("]"); + return builder.toString(); + } + + private CallCapabilities() {} } diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java index a254459..0b5981c 100644 --- a/telecomm/java/android/telecomm/CallService.java +++ b/telecomm/java/android/telecomm/CallService.java @@ -61,7 +61,7 @@ public abstract class CallService extends Service { private static final int MSG_ON_AUDIO_STATE_CHANGED = 11; private static final int MSG_PLAY_DTMF_TONE = 12; private static final int MSG_STOP_DTMF_TONE = 13; - private static final int MSG_ADD_TO_CONFERENCE = 14; + private static final int MSG_CONFERENCE = 14; private static final int MSG_SPLIT_FROM_CONFERENCE = 15; private static final int MSG_ON_POST_DIAL_CONTINUE = 16; @@ -128,24 +128,12 @@ public abstract class CallService extends Service { case MSG_STOP_DTMF_TONE: stopDtmfTone((String) msg.obj); break; - case MSG_ADD_TO_CONFERENCE: { - SomeArgs args = (SomeArgs) msg.obj; - try { - @SuppressWarnings("unchecked") - List<String> callIds = (List<String>) args.arg2; - String conferenceCallId = (String) args.arg1; - addToConference(conferenceCallId, callIds); - } finally { - args.recycle(); - } - break; - } - case MSG_SPLIT_FROM_CONFERENCE: { + case MSG_CONFERENCE: { SomeArgs args = (SomeArgs) msg.obj; try { String conferenceCallId = (String) args.arg1; String callId = (String) args.arg2; - splitFromConference(conferenceCallId, callId); + conference(conferenceCallId, callId); } finally { args.recycle(); } @@ -162,6 +150,9 @@ public abstract class CallService extends Service { } break; } + case MSG_SPLIT_FROM_CONFERENCE: + splitFromConference((String) msg.obj); + break; default: break; } @@ -245,19 +236,16 @@ public abstract class CallService extends Service { } @Override - public void addToConference(String conferenceCallId, List<String> callsToConference) { + public void conference(String conferenceCallId, String callId) { SomeArgs args = SomeArgs.obtain(); args.arg1 = conferenceCallId; - args.arg2 = callsToConference; - mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget(); + args.arg2 = callId; + mMessageHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget(); } @Override - public void splitFromConference(String conferenceCallId, String callId) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = conferenceCallId; - args.arg2 = callId; - mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget(); + public void splitFromConference(String callId) { + mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget(); } @Override @@ -424,24 +412,22 @@ public abstract class CallService extends Service { public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState); /** - * Adds the specified calls to the specified conference call. + * Conferences the specified call. * * @param conferenceCallId The unique ID of the conference call onto which the specified calls * should be added. - * @param callIds The calls to add to the conference call. + * @param callId The call to conference. * @hide */ - public abstract void addToConference(String conferenceCallId, List<String> callIds); + public abstract void conference(String conferenceCallId, String callId); /** - * Removes the specified call from the specified conference call. This is a no-op if the call - * is not already part of the conference call. + * Removes the specified call from a conference call. * - * @param conferenceCallId The conference call. * @param callId The call to remove from the conference call * @hide */ - public abstract void splitFromConference(String conferenceCallId, String callId); + public abstract void splitFromConference(String callId); public void onPostDialContinue(String callId, boolean proceed) {} public void onPostDialWait(Connection conn, String remaining) {} diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java index 8c3ddad..0ba8161 100644 --- a/telecomm/java/android/telecomm/CallServiceAdapter.java +++ b/telecomm/java/android/telecomm/CallServiceAdapter.java @@ -179,12 +179,12 @@ public final class CallServiceAdapter { * Indicates that the specified call can conference with any of the specified list of calls. * * @param callId The unique ID of the call. - * @param conferenceCapableCallIds The unique IDs of the calls which can be conferenced. + * @param canConference Specified whether or not the call can be conferenced. * @hide */ - public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) { + public void setCanConference(String callId, boolean canConference) { try { - mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds); + mAdapter.setCanConference(callId, canConference); } catch (RemoteException ignored) { } } @@ -193,13 +193,14 @@ public final class CallServiceAdapter { * Indicates whether or not the specified call is currently conferenced into the specified * conference call. * - * @param conferenceCallId The unique ID of the conference call. * @param callId The unique ID of the call being conferenced. + * @param conferenceCallId The unique ID of the conference call. Null if call is not + * conferenced. * @hide */ - public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) { + public void setIsConferenced(String callId, String conferenceCallId) { try { - mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced); + mAdapter.setIsConferenced(callId, conferenceCallId); } catch (RemoteException ignored) { } } @@ -236,4 +237,16 @@ public final class CallServiceAdapter { } catch (RemoteException e) { } } + + /** + * Indicates that a new conference call has been created. + * + * @param callId The unique ID of the conference call. + */ + public void addConferenceCall(String callId) { + try { + mAdapter.addConferenceCall(callId, null); + } catch (RemoteException ignored) { + } + } } diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java index 344814f..7aee770 100644 --- a/telecomm/java/android/telecomm/Connection.java +++ b/telecomm/java/android/telecomm/Connection.java @@ -19,7 +19,10 @@ package android.telecomm; import android.net.Uri; import android.os.Bundle; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -35,6 +38,8 @@ public abstract class Connection { void onDisconnected(Connection c, int cause, String message); void onRequestingRingback(Connection c, boolean ringback); void onDestroyed(Connection c); + void onConferenceCapableChanged(Connection c, boolean isConferenceCapable); + void onParentConnectionChanged(Connection c, Connection parent); } public static class ListenerBase implements Listener { @@ -65,6 +70,14 @@ public abstract class Connection { /** {@inheritDoc} */ @Override public void onRequestingRingback(Connection c, boolean ringback) {} + + /** ${inheritDoc} */ + @Override + public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) {} + + /** ${inheritDoc} */ + @Override + public void onParentConnectionChanged(Connection c, Connection parent) {} } public final class State { @@ -79,10 +92,14 @@ public abstract class Connection { } private final Set<Listener> mListeners = new HashSet<>(); + private final List<Connection> mChildConnections = new ArrayList<>(); + private int mState = State.NEW; private CallAudioState mCallAudioState; private Uri mHandle; private boolean mRequestingRingback = false; + private boolean mIsConferenceCapable = false; + private Connection mParentConnection; /** * Create a new Connection. @@ -176,6 +193,16 @@ public abstract class Connection { } /** + * Separates this Connection from a parent connection. + * + * @hide + */ + public final void separate() { + Log.d(this, "separate"); + onSeparate(); + } + + /** * Abort this Connection. The Connection will immediately transition to * the {@link State#DISCONNECTED} state, and send no notifications of this * or any other future events. @@ -240,6 +267,14 @@ public abstract class Connection { } /** + * TODO(santoscordon): Needs updated documentation. + */ + public final void conference() { + Log.d(this, "conference"); + onConference(); + } + + /** * Inform this Connection that the state of its audio output has been changed externally. * * @param state The new audio state. @@ -274,7 +309,7 @@ public abstract class Connection { } /** - * @return Whether this connection is requesting that the system play a ringback tone + * Returns whether this connection is requesting that the system play a ringback tone * on its behalf. */ public boolean isRequestingRingback() { @@ -282,6 +317,38 @@ public abstract class Connection { } /** + * Returns whether this connection is a conference connection (has child connections). + */ + public boolean isConferenceConnection() { + return !mChildConnections.isEmpty(); + } + + public void setParentConnection(Connection parentConnection) { + Log.d(this, "parenting %s to %s", this, parentConnection); + if (mParentConnection != parentConnection) { + if (mParentConnection != null) { + mParentConnection.removeChild(this); + } + mParentConnection = parentConnection; + if (mParentConnection != null) { + mParentConnection.addChild(this); + // do something if the child connections goes down to ZERO. + } + for (Listener l : mListeners) { + l.onParentConnectionChanged(this, mParentConnection); + } + } + } + + public Connection getParentConnection() { + return mParentConnection; + } + + public List<Connection> getChildConnections() { + return mChildConnections; + } + + /** * Sets the value of the {@link #getHandle()} property and notifies listeners. * * @param handle The new handle. @@ -359,6 +426,32 @@ public abstract class Connection { } /** + * TODO(santoscordon): Needs documentation. + */ + protected void setIsConferenceCapable(boolean isConferenceCapable) { + if (mIsConferenceCapable != isConferenceCapable) { + mIsConferenceCapable = isConferenceCapable; + for (Listener l : mListeners) { + l.onConferenceCapableChanged(this, mIsConferenceCapable); + } + } + } + + /** + * TODO(santoscordon): Needs documentation. + */ + protected void setDestroyed() { + // It is possible that onDestroy() will trigger the listener to remove itself which will + // result in a concurrent modification exception. To counteract this we make a copy of the + // listeners and iterate on that. + for (Listener l : new ArrayList<>(mListeners)) { + if (mListeners.contains(l)) { + l.onDestroyed(this); + } + } + } + + /** * Notifies this Connection and listeners that the {@link #getCallAudioState()} property * has a new value. * @@ -418,6 +511,11 @@ public abstract class Connection { protected void onDisconnect() {} /** + * Notifies this Connection of a request to disconnect. + */ + protected void onSeparate() {} + + /** * Notifies this Connection of a request to abort. */ protected void onAbort() {} @@ -449,6 +547,28 @@ public abstract class Connection { */ protected void onPostDialContinue(boolean proceed) {} + /** + * TODO(santoscordon): Needs documentation. + */ + protected void onConference() {} + + /** + * TODO(santoscordon): Needs documentation. + */ + protected void onChildrenChanged(List<Connection> children) {} + + private void addChild(Connection connection) { + Log.d(this, "adding child %s", connection); + mChildConnections.add(connection); + onChildrenChanged(mChildConnections); + } + + private void removeChild(Connection connection) { + Log.d(this, "removing child %s", connection); + mChildConnections.remove(connection); + onChildrenChanged(mChildConnections); + } + private void setState(int state) { Log.d(this, "setState: %s", stateToString(state)); onSetState(state); diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java index 59e977d..9dfad2d 100644 --- a/telecomm/java/android/telecomm/ConnectionService.java +++ b/telecomm/java/android/telecomm/ConnectionService.java @@ -20,10 +20,15 @@ import android.net.Uri; import android.os.Bundle; import android.telephony.DisconnectCause; +import android.os.SystemClock; + +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; /** * A {@link android.app.Service} that provides telephone connections to @@ -32,7 +37,6 @@ import java.util.Map; public abstract class ConnectionService extends CallService { // Flag controlling whether PII is emitted into the logs private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG); - private static final Connection NULL_CONNECTION = new Connection() {}; // Mappings from Connections to IDs as understood by the current CallService implementation @@ -99,6 +103,20 @@ public abstract class ConnectionService extends CallService { Log.d(this, "Adapter onRingback %b", ringback); getAdapter().setRequestingRingback(id, ringback); } + + @Override + public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) { + String id = mIdByConnection.get(c); + getAdapter().setCanConference(id, isConferenceCapable); + } + + /** ${inheritDoc} */ + @Override + public void onParentConnectionChanged(Connection c, Connection parent) { + String id = mIdByConnection.get(c); + String parentId = parent == null ? null : mIdByConnection.get(parent); + getAdapter().setIsConferenced(id, parentId); + } }; @Override @@ -110,8 +128,7 @@ public abstract class ConnectionService extends CallService { @Override public void onResult(Uri handle, Subscription... result) { boolean isCompatible = result.length > 0; - Log.d(this, "adapter setIsCompatibleWith " - + callInfo.getId() + " " + isCompatible); + Log.d(this, "adapter setIsCompatibleWith "); getAdapter().setIsCompatibleWith(callInfo.getId(), isCompatible); } @@ -135,7 +152,7 @@ public abstract class ConnectionService extends CallService { new Response<ConnectionRequest, Connection>() { @Override public void onResult(ConnectionRequest request, Connection... result) { - if (result.length != 1) { + if (result != null && result.length != 1) { Log.d(this, "adapter handleFailedOutgoingCall %s", callInfo); getAdapter().handleFailedOutgoingCall( request, @@ -145,10 +162,10 @@ public abstract class ConnectionService extends CallService { c.abort(); } } else { - addConnection(callInfo.getId(), result[0]); Log.d(this, "adapter handleSuccessfulOutgoingCall %s", callInfo.getId()); getAdapter().handleSuccessfulOutgoingCall(callInfo.getId()); + addConnection(callInfo.getId(), result[0]); } } @@ -177,7 +194,7 @@ public abstract class ConnectionService extends CallService { new Response<ConnectionRequest, Connection>() { @Override public void onResult(ConnectionRequest request, Connection... result) { - if (result.length != 1) { + if (result != null && result.length != 1) { Log.d(this, "adapter handleFailedOutgoingCall %s", callId); getAdapter().handleFailedOutgoingCall( request, @@ -258,27 +275,43 @@ public abstract class ConnectionService extends CallService { /** @hide */ @Override - public final void addToConference(String conferenceCallId, List<String> callIds) { - Log.d(this, "addToConference %s, %s", conferenceCallId, callIds); - - List<Connection> connections = new LinkedList<>(); - for (String id : callIds) { - Connection connection = findConnectionForAction(id, "addToConference"); - if (connection == NULL_CONNECTION) { - Log.w(this, "Connection missing in conference request %s.", id); - return; - } - connections.add(connection); + public final void conference(final String conferenceCallId, String callId) { + Log.d(this, "conference %s, %s", conferenceCallId, callId); + + Connection connection = findConnectionForAction(callId, "conference"); + if (connection == NULL_CONNECTION) { + Log.w(this, "Connection missing in conference request %s.", callId); + return; } - // TODO(santoscordon): Find an existing conference call or create a new one. Then call - // conferenceWith on it. + onCreateConferenceConnection(conferenceCallId, connection, + new Response<String, Connection>() { + /** ${inheritDoc} */ + @Override + public void onResult(String ignored, Connection... result) { + Log.d(this, "onCreateConference.Response %s", (Object[]) result); + if (result != null && result.length == 1) { + Connection conferenceConnection = result[0]; + if (!mIdByConnection.containsKey(conferenceConnection)) { + Log.v(this, "sending new conference call %s", conferenceCallId); + getAdapter().addConferenceCall(conferenceCallId); + addConnection(conferenceCallId, conferenceConnection); + } + } + } + + /** ${inheritDoc} */ + @Override + public void onError(String request, int code, String reason) { + // no-op + } + }); } /** @hide */ @Override - public final void splitFromConference(String conferenceCallId, String callId) { - Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId); + public final void splitFromConference(String callId) { + Log.d(this, "splitFromConference(%s)", callId); Connection connection = findConnectionForAction(callId, "splitFromConference"); if (connection == NULL_CONNECTION) { @@ -309,6 +342,13 @@ public abstract class ConnectionService extends CallService { } /** + * Returns all connections currently associated with this connection service. + */ + public Collection<Connection> getAllConnections() { + return mConnectionById.values(); + } + + /** * Find a set of Subscriptions matching a given handle (e.g. phone number). * * @param handle A handle (e.g. phone number) with which to connect. @@ -329,6 +369,21 @@ public abstract class ConnectionService extends CallService { Response<ConnectionRequest, Connection> callback) {} /** + * Returns a new or existing conference connection when the the user elects to convert the + * specified connection into a conference call. The specified connection can be any connection + * which had previously specified itself as conference-capable including both simple connections + * and connections previously returned from this method. + * + * @param connection The connection from which the user opted to start a conference call. + * @param token The token to be passed into the response callback. + * @param callback The callback for providing the potentially-new conference connection. + */ + public void onCreateConferenceConnection( + String token, + Connection connection, + Response<String, Connection> callback) {} + + /** * Create a Connection to match an incoming connection notification. * * @param request Data encapsulating details of the desired Connection. @@ -338,6 +393,20 @@ public abstract class ConnectionService extends CallService { ConnectionRequest request, Response<ConnectionRequest, Connection> callback) {} + /** + * Notifies that a connection has been added to this connection service and sent to Telecomm. + * + * @param connection The connection which was added. + */ + public void onConnectionAdded(Connection connection) {} + + /** + * Notified that a connection has been removed from this connection service. + * + * @param connection The connection which was removed. + */ + public void onConnectionRemoved(Connection connection) {} + static String toLogSafePhoneNumber(String number) { // For unknown number, log empty string. if (number == null) { @@ -387,12 +456,14 @@ public abstract class ConnectionService extends CallService { mConnectionById.put(callId, connection); mIdByConnection.put(connection, callId); connection.addConnectionListener(mConnectionListener); + onConnectionAdded(connection); } private void removeConnection(Connection connection) { connection.removeConnectionListener(mConnectionListener); mConnectionById.remove(mIdByConnection.get(connection)); mIdByConnection.remove(connection); + onConnectionRemoved(connection); } private Connection findConnectionForAction(String callId, String action) { diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java index 0bef419..86b7a50 100644 --- a/telecomm/java/android/telecomm/InCallAdapter.java +++ b/telecomm/java/android/telecomm/InCallAdapter.java @@ -199,15 +199,14 @@ public final class InCallAdapter { } /** - * Instructs Telecomm to conference the specified calls together. + * Instructs Telecomm to conference the specified call. * * @param callId The unique ID of the call. - * @param callIdToConference The unique ID of the call to conference with. * @hide */ - void conferenceWith(String callId, String callIdToConference) { + public void conference(String callId) { try { - mAdapter.conferenceWith(callId, callIdToConference); + mAdapter.conference(callId); } catch (RemoteException ignored) { } } @@ -219,7 +218,7 @@ public final class InCallAdapter { * @param callId The unique ID of the call. * @hide */ - void splitFromConference(String callId) { + public void splitFromConference(String callId) { try { mAdapter.splitFromConference(callId); } catch (RemoteException ignored) { diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java index b531ccd..432e378 100644 --- a/telecomm/java/android/telecomm/InCallCall.java +++ b/telecomm/java/android/telecomm/InCallCall.java @@ -39,7 +39,6 @@ public final class InCallCall implements Parcelable { private final GatewayInfo mGatewayInfo; private final CallServiceDescriptor mCurrentCallServiceDescriptor; private final CallServiceDescriptor mHandoffCallServiceDescriptor; - private final List<String> mConferenceCapableCallIds; private final String mParentCallId; private final List<String> mChildCallIds; @@ -57,8 +56,7 @@ public final class InCallCall implements Parcelable { CallServiceDescriptor descriptor, CallServiceDescriptor handoffDescriptor) { this(id, state, disconnectCauseCode, disconnectCauseMsg, capabilities, connectTimeMillis, - handle, gatewayInfo, descriptor, handoffDescriptor, Collections.EMPTY_LIST, null, - Collections.EMPTY_LIST); + handle, gatewayInfo, descriptor, handoffDescriptor, null, Collections.EMPTY_LIST); } /** @hide */ @@ -73,7 +71,6 @@ public final class InCallCall implements Parcelable { GatewayInfo gatewayInfo, CallServiceDescriptor descriptor, CallServiceDescriptor handoffDescriptor, - List<String> conferenceCapableCallIds, String parentCallId, List<String> childCallIds) { mId = id; @@ -86,7 +83,6 @@ public final class InCallCall implements Parcelable { mGatewayInfo = gatewayInfo; mCurrentCallServiceDescriptor = descriptor; mHandoffCallServiceDescriptor = handoffDescriptor; - mConferenceCapableCallIds = conferenceCapableCallIds; mParentCallId = parentCallId; mChildCallIds = childCallIds; } @@ -151,14 +147,6 @@ public final class InCallCall implements Parcelable { } /** - * The calls with which this call can conference. - * @hide - */ - public List<String> getConferenceCapableCallIds() { - return mConferenceCapableCallIds; - } - - /** * The conference call to which this call is conferenced. Null if not conferenced. * @hide */ @@ -191,14 +179,12 @@ public final class InCallCall implements Parcelable { GatewayInfo gatewayInfo = source.readParcelable(classLoader); CallServiceDescriptor descriptor = source.readParcelable(classLoader); CallServiceDescriptor handoffDescriptor = source.readParcelable(classLoader); - List<String> conferenceCapableCallIds = new ArrayList<>(); - source.readList(conferenceCapableCallIds, classLoader); String parentCallId = source.readString(); List<String> childCallIds = new ArrayList<>(); source.readList(childCallIds, classLoader); return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg, capabilities, connectTimeMillis, handle, gatewayInfo, descriptor, handoffDescriptor, - conferenceCapableCallIds, parentCallId, childCallIds); + parentCallId, childCallIds); } @Override @@ -226,8 +212,12 @@ public final class InCallCall implements Parcelable { destination.writeParcelable(mGatewayInfo, 0); destination.writeParcelable(mCurrentCallServiceDescriptor, 0); destination.writeParcelable(mHandoffCallServiceDescriptor, 0); - destination.writeList(mConferenceCapableCallIds); destination.writeString(mParentCallId); destination.writeList(mChildCallIds); } + + @Override + public String toString() { + return String.format("[%s, parent:%s, children:%s]", mId, mParentCallId, mChildCallIds); + } } diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl index 9139aa6..827f331 100644 --- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl +++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl @@ -56,9 +56,9 @@ oneway interface ICallService { void stopDtmfTone(String callId); - void addToConference(String conferenceCallId, in List<String> callIds); + void conference(String conferenceCallId, String callId); - void splitFromConference(String conferenceCallId, String callId); + void splitFromConference(String callId); void onPostDialContinue(String callId, boolean proceed); } diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl index b380293..6e176eb 100644 --- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl @@ -47,9 +47,11 @@ oneway interface ICallServiceAdapter { void setRequestingRingback(String callId, boolean ringing); - void setCanConferenceWith(String callId, in List<String> conferenceCapableCallIds); + void setCanConference(String callId, boolean canConference); - void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced); + void setIsConferenced(String callId, String conferenceCallId); + + void addConferenceCall(String callId, in CallInfo callInfo); void removeCall(String callId); diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl index f144043..5717456 100644 --- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl @@ -48,7 +48,7 @@ oneway interface IInCallAdapter { void handoffCall(String callId); - void conferenceWith(String callId, String callIdToConference); + void conference(String callId); void splitFromConference(String callId); } diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl index 3e8db06..6fc64ae 100644 --- a/telephony/java/com/android/internal/telephony/ISms.aidl +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -119,6 +119,19 @@ interface ISms { in PendingIntent sentIntent, in PendingIntent deliveryIntent); /** + * Inject an SMS PDU into the android platform. + * + * @param pdu is the byte array of pdu to be injected into android application framework + * @param format is the format of SMS pdu (android.telephony.SmsMessage.FORMAT_3GPP or + * android.telephony.SmsMessage.FORMAT_3GPP2) + * @param receivedIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully received by the + * android application framework. This intent is broadcasted at + * the same time an SMS received from radio is acknowledged back. + */ + void injectSmsPdu(in byte[] pdu, String format, in PendingIntent receivedIntent); + + /** * Send a multi-part text based SMS. * * @param destinationAddress the address to send the message to diff --git a/tests/Camera2Tests/CameraToo/Android.mk b/tests/Camera2Tests/CameraToo/Android.mk new file mode 100644 index 0000000..7e5911d --- /dev/null +++ b/tests/Camera2Tests/CameraToo/Android.mk @@ -0,0 +1,23 @@ +# Copyright (C) 2014 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := CameraToo +LOCAL_SDK_VERSION := current +LOCAL_SRC_FILES := $(call all-java-files-under,src) + +include $(BUILD_PACKAGE) diff --git a/tests/Camera2Tests/CameraToo/AndroidManifest.xml b/tests/Camera2Tests/CameraToo/AndroidManifest.xml new file mode 100644 index 0000000..a92b5d8 --- /dev/null +++ b/tests/Camera2Tests/CameraToo/AndroidManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (C) 2014 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.camera2.cameratoo"> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <application android:label="CameraToo"> + <activity + android:name=".CameraTooActivity" + android:screenOrientation="portrait"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml b/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml new file mode 100644 index 0000000..f93f177 --- /dev/null +++ b/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (C) 2014 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. +--> + +<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/mainSurfaceView" + android:layout_height="fill_parent" + android:layout_width="fill_parent" + android:onClick="onClickOnSurfaceView" /> diff --git a/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java b/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java new file mode 100644 index 0000000..c630bad --- /dev/null +++ b/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2014 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.example.android.camera2.cameratoo; + +import android.app.Activity; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.Image; +import android.media.ImageReader; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.util.Size; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * A basic demonstration of how to write a point-and-shoot camera app against the new + * android.hardware.camera2 API. + */ +public class CameraTooActivity extends Activity { + /** Output files will be saved as /sdcard/Pictures/cameratoo*.jpg */ + static final String CAPTURE_FILENAME_PREFIX = "cameratoo"; + /** Tag to distinguish log prints. */ + static final String TAG = "CameraToo"; + + /** An additional thread for running tasks that shouldn't block the UI. */ + HandlerThread mBackgroundThread; + /** Handler for running tasks in the background. */ + Handler mBackgroundHandler; + /** Handler for running tasks on the UI thread. */ + Handler mForegroundHandler; + /** View for displaying the camera preview. */ + SurfaceView mSurfaceView; + /** Used to retrieve the captured image when the user takes a snapshot. */ + ImageReader mCaptureBuffer; + /** Handle to the Android camera services. */ + CameraManager mCameraManager; + /** The specific camera device that we're using. */ + CameraDevice mCamera; + /** Our image capture session. */ + CameraCaptureSession mCaptureSession; + + /** + * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose + * width and height are at least as large as the respective requested values. + * @param choices The list of sizes that the camera supports for the intended output class + * @param width The minimum desired width + * @param height The minimum desired height + * @return The optimal {@code Size}, or an arbitrary one if none were big enough + */ + static Size chooseBigEnoughSize(Size[] choices, int width, int height) { + // Collect the supported resolutions that are at least as big as the preview Surface + List<Size> bigEnough = new ArrayList<Size>(); + for (Size option : choices) { + if (option.getWidth() >= width && option.getHeight() >= height) { + bigEnough.add(option); + } + } + + // Pick the smallest of those, assuming we found any + if (bigEnough.size() > 0) { + return Collections.min(bigEnough, new CompareSizesByArea()); + } else { + Log.e(TAG, "Couldn't find any suitable preview size"); + return choices[0]; + } + } + + /** + * Compares two {@code Size}s based on their areas. + */ + static class CompareSizesByArea implements Comparator<Size> { + @Override + public int compare(Size lhs, Size rhs) { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + } + } + + /** + * Called when our {@code Activity} gains focus. <p>Starts initializing the camera.</p> + */ + @Override + protected void onResume() { + super.onResume(); + + // Start a background thread to manage camera requests + mBackgroundThread = new HandlerThread("background"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + mForegroundHandler = new Handler(getMainLooper()); + + mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE); + + // Inflate the SurfaceView, set it as the main layout, and attach a listener + View layout = getLayoutInflater().inflate(R.layout.mainactivity, null); + mSurfaceView = (SurfaceView) layout.findViewById(R.id.mainSurfaceView); + mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback); + setContentView(mSurfaceView); + + // Control flow continues in mSurfaceHolderCallback.surfaceChanged() + } + + /** + * Called when our {@code Activity} loses focus. <p>Tears everything back down.</p> + */ + @Override + protected void onPause() { + super.onPause(); + + try { + // Ensure SurfaceHolderCallback#surfaceChanged() will run again if the user returns + mSurfaceView.getHolder().setFixedSize(/*width*/0, /*height*/0); + + // Cancel any stale preview jobs + if (mCaptureSession != null) { + mCaptureSession.close(); + mCaptureSession = null; + } + } finally { + if (mCamera != null) { + mCamera.close(); + mCamera = null; + } + } + + // Finish processing posted messages, then join on the handling thread + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + } catch (InterruptedException ex) { + Log.e(TAG, "Background worker thread was interrupted while joined", ex); + } + + // Close the ImageReader now that the background thread has stopped + if (mCaptureBuffer != null) mCaptureBuffer.close(); + } + + /** + * Called when the user clicks on our {@code SurfaceView}, which has ID {@code mainSurfaceView} + * as defined in the {@code mainactivity.xml} layout file. <p>Captures a full-resolution image + * and saves it to permanent storage.</p> + */ + public void onClickOnSurfaceView(View v) { + if (mCaptureSession != null) { + try { + CaptureRequest.Builder requester = + mCamera.createCaptureRequest(mCamera.TEMPLATE_STILL_CAPTURE); + requester.addTarget(mCaptureBuffer.getSurface()); + try { + // This handler can be null because we aren't actually attaching any callback + mCaptureSession.capture(requester.build(), /*listener*/null, /*handler*/null); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to file actual capture request", ex); + } + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to build actual capture request", ex); + } + } else { + Log.e(TAG, "User attempted to perform a capture outside our session"); + } + + // Control flow continues in mImageCaptureListener.onImageAvailable() + } + + /** + * Callbacks invoked upon state changes in our {@code SurfaceView}. + */ + final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { + /** The camera device to use, or null if we haven't yet set a fixed surface size. */ + private String mCameraId; + + /** Whether we received a change callback after setting our fixed surface size. */ + private boolean mGotSecondCallback; + + @Override + public void surfaceCreated(SurfaceHolder holder) { + // This is called every time the surface returns to the foreground + Log.i(TAG, "Surface created"); + mCameraId = null; + mGotSecondCallback = false; + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.i(TAG, "Surface destroyed"); + holder.removeCallback(this); + // We don't stop receiving callbacks forever because onResume() will reattach us + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + // On the first invocation, width and height were automatically set to the view's size + if (mCameraId == null) { + // Find the device's back-facing camera and set the destination buffer sizes + try { + for (String cameraId : mCameraManager.getCameraIdList()) { + CameraCharacteristics cameraCharacteristics = + mCameraManager.getCameraCharacteristics(cameraId); + if (cameraCharacteristics.get(cameraCharacteristics.LENS_FACING) == + CameraCharacteristics.LENS_FACING_BACK) { + Log.i(TAG, "Found a back-facing camera"); + StreamConfigurationMap info = cameraCharacteristics + .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + + // Bigger is better when it comes to saving our image + Size largestSize = Collections.max( + Arrays.asList(info.getOutputSizes(ImageFormat.JPEG)), + new CompareSizesByArea()); + + // Prepare an ImageReader in case the user wants to capture images + Log.i(TAG, "Capture size: " + largestSize); + mCaptureBuffer = ImageReader.newInstance(largestSize.getWidth(), + largestSize.getHeight(), ImageFormat.JPEG, /*maxImages*/2); + mCaptureBuffer.setOnImageAvailableListener( + mImageCaptureListener, mBackgroundHandler); + + // Danger, W.R.! Attempting to use too large a preview size could + // exceed the camera bus' bandwidth limitation, resulting in + // gorgeous previews but the storage of garbage capture data. + Log.i(TAG, "SurfaceView size: " + + mSurfaceView.getWidth() + 'x' + mSurfaceView.getHeight()); + Size optimalSize = chooseBigEnoughSize( + info.getOutputSizes(SurfaceHolder.class), width, height); + + // Set the SurfaceHolder to use the camera's largest supported size + Log.i(TAG, "Preview size: " + optimalSize); + SurfaceHolder surfaceHolder = mSurfaceView.getHolder(); + surfaceHolder.setFixedSize(optimalSize.getWidth(), + optimalSize.getHeight()); + + mCameraId = cameraId; + return; + + // Control flow continues with this method one more time + // (since we just changed our own size) + } + } + } catch (CameraAccessException ex) { + Log.e(TAG, "Unable to list cameras", ex); + } + + Log.e(TAG, "Didn't find any back-facing cameras"); + // This is the second time the method is being invoked: our size change is complete + } else if (!mGotSecondCallback) { + if (mCamera != null) { + Log.e(TAG, "Aborting camera open because it hadn't been closed"); + return; + } + + // Open the camera device + try { + mCameraManager.openCamera(mCameraId, mCameraStateListener, + mBackgroundHandler); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to configure output surface", ex); + } + mGotSecondCallback = true; + + // Control flow continues in mCameraStateListener.onOpened() + } + }}; + + /** + * Calledbacks invoked upon state changes in our {@code CameraDevice}. <p>These are run on + * {@code mBackgroundThread}.</p> + */ + final CameraDevice.StateListener mCameraStateListener = + new CameraDevice.StateListener() { + @Override + public void onOpened(CameraDevice camera) { + Log.i(TAG, "Successfully opened camera"); + mCamera = camera; + try { + List<Surface> outputs = Arrays.asList( + mSurfaceView.getHolder().getSurface(), mCaptureBuffer.getSurface()); + camera.createCaptureSession(outputs, mCaptureSessionListener, + mBackgroundHandler); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to create a capture session", ex); + } + + // Control flow continues in mCaptureSessionListener.onConfigured() + } + + @Override + public void onDisconnected(CameraDevice camera) { + Log.e(TAG, "Camera was disconnected"); + } + + @Override + public void onError(CameraDevice camera, int error) { + Log.e(TAG, "State error on device '" + camera.getId() + "': code " + error); + }}; + + /** + * Callbacks invoked upon state changes in our {@code CameraCaptureSession}. <p>These are run on + * {@code mBackgroundThread}.</p> + */ + final CameraCaptureSession.StateListener mCaptureSessionListener = + new CameraCaptureSession.StateListener() { + @Override + public void onConfigured(CameraCaptureSession session) { + Log.i(TAG, "Finished configuring camera outputs"); + mCaptureSession = session; + + SurfaceHolder holder = mSurfaceView.getHolder(); + if (holder != null) { + try { + // Build a request for preview footage + CaptureRequest.Builder requestBuilder = + mCamera.createCaptureRequest(mCamera.TEMPLATE_PREVIEW); + requestBuilder.addTarget(holder.getSurface()); + CaptureRequest previewRequest = requestBuilder.build(); + + // Start displaying preview images + try { + session.setRepeatingRequest(previewRequest, /*listener*/null, + /*handler*/null); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to make repeating preview request", ex); + } + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to build preview request", ex); + } + } + else { + Log.e(TAG, "Holder didn't exist when trying to formulate preview request"); + } + } + + @Override + public void onClosed(CameraCaptureSession session) { + mCaptureSession = null; + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) { + Log.e(TAG, "Configuration error on device '" + mCamera.getId()); + }}; + + /** + * Callback invoked when we've received a JPEG image from the camera. + */ + final ImageReader.OnImageAvailableListener mImageCaptureListener = + new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + // Save the image once we get a chance + mBackgroundHandler.post(new CapturedImageSaver(reader.acquireNextImage())); + + // Control flow continues in CapturedImageSaver#run() + }}; + + /** + * Deferred processor responsible for saving snapshots to disk. <p>This is run on + * {@code mBackgroundThread}.</p> + */ + static class CapturedImageSaver implements Runnable { + /** The image to save. */ + private Image mCapture; + + public CapturedImageSaver(Image capture) { + mCapture = capture; + } + + @Override + public void run() { + try { + // Choose an unused filename under the Pictures/ directory + File file = File.createTempFile(CAPTURE_FILENAME_PREFIX, ".jpg", + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES)); + try (FileOutputStream ostream = new FileOutputStream(file)) { + Log.i(TAG, "Retrieved image is" + + (mCapture.getFormat() == ImageFormat.JPEG ? "" : "n't") + " a JPEG"); + ByteBuffer buffer = mCapture.getPlanes()[0].getBuffer(); + Log.i(TAG, "Captured image size: " + + mCapture.getWidth() + 'x' + mCapture.getHeight()); + + // Write the image out to the chosen file + byte[] jpeg = new byte[buffer.remaining()]; + buffer.get(jpeg); + ostream.write(jpeg); + } catch (FileNotFoundException ex) { + Log.e(TAG, "Unable to open output file for writing", ex); + } catch (IOException ex) { + Log.e(TAG, "Failed to write the image to the output file", ex); + } + } catch (IOException ex) { + Log.e(TAG, "Unable to create a new output file", ex); + } finally { + mCapture.close(); + } + } + } +} diff --git a/tests/Camera2Tests/CameraToo/tests/Android.mk b/tests/Camera2Tests/CameraToo/tests/Android.mk new file mode 100644 index 0000000..0b58243 --- /dev/null +++ b/tests/Camera2Tests/CameraToo/tests/Android.mk @@ -0,0 +1,25 @@ +# Copyright (C) 2014 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := CameraTooTests +LOCAL_INSTRUMENTATION_FOR := CameraToo +LOCAL_SDK_VERSION := current +LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target + +include $(BUILD_PACKAGE) diff --git a/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml b/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml new file mode 100644 index 0000000..30210ba --- /dev/null +++ b/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + Copyright (C) 2014 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.camera2.cameratoo.tests"> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <application android:label="CameraToo"> + <uses-library android:name="android.test.runner" /> + </application> + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.example.android.camera2.cameratoo" + android:label="CameraToo tests" /> +</manifest> diff --git a/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java b/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java new file mode 100644 index 0000000..3acca5a --- /dev/null +++ b/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2014 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.example.android.camera2.cameratoo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.media.Image; +import android.os.Environment; +import android.util.Size; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FilenameFilter; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + +import com.example.android.camera2.cameratoo.CameraTooActivity; +import org.junit.Test; + +public class CameraTooTest { + private <T> void assertComparatorEq(T lhs, T rhs, Comparator<T> rel) { + assertEquals(String.format("%s should be equal to %s", lhs, rhs), rel.compare(lhs, rhs), 0); + assertEquals(String.format("%s should be equal to %s (reverse check)", lhs, rhs), + rel.compare(rhs, lhs), 0); + } + + private <T> void assertComparatorLt(T lhs, T rhs, Comparator<T> rel) { + assertTrue(String.format("%s should be less than %s", lhs, rhs), rel.compare(lhs, rhs) < 0); + assertTrue(String.format("%s should be less than %s (reverse check)", lhs, rhs), + rel.compare(rhs, lhs) > 0); + } + + @Test + public void compareSizesByArea() { + Size empty = new Size(0, 0), fatAndFlat = new Size(100, 0), tallAndThin = new Size(0, 100); + Size smallSquare = new Size(4, 4), horizRect = new Size(8, 2), vertRect = new Size(2, 8); + Size largeSquare = new Size(5, 5); + Comparator<Size> rel = new CameraTooActivity.CompareSizesByArea(); + + assertComparatorEq(empty, fatAndFlat, rel); + assertComparatorEq(empty, tallAndThin, rel); + assertComparatorEq(fatAndFlat, empty, rel); + assertComparatorEq(fatAndFlat, tallAndThin, rel); + assertComparatorEq(tallAndThin, empty, rel); + assertComparatorEq(tallAndThin, fatAndFlat, rel); + + assertComparatorEq(smallSquare, horizRect, rel); + assertComparatorEq(smallSquare, vertRect, rel); + assertComparatorEq(horizRect, smallSquare, rel); + assertComparatorEq(horizRect, vertRect, rel); + assertComparatorEq(vertRect, smallSquare, rel); + assertComparatorEq(vertRect, horizRect, rel); + + assertComparatorLt(empty, smallSquare, rel); + assertComparatorLt(empty, horizRect, rel); + assertComparatorLt(empty, vertRect, rel); + + assertComparatorLt(fatAndFlat, smallSquare, rel); + assertComparatorLt(fatAndFlat, horizRect, rel); + assertComparatorLt(fatAndFlat, vertRect, rel); + + assertComparatorLt(tallAndThin, smallSquare, rel); + assertComparatorLt(tallAndThin, horizRect, rel); + assertComparatorLt(tallAndThin, vertRect, rel); + + assertComparatorLt(empty, largeSquare, rel); + assertComparatorLt(fatAndFlat, largeSquare, rel); + assertComparatorLt(tallAndThin, largeSquare, rel); + assertComparatorLt(smallSquare, largeSquare, rel); + assertComparatorLt(horizRect, largeSquare, rel); + assertComparatorLt(vertRect, largeSquare, rel); + } + + private void assertOptimalSize(Size[] options, int minWidth, int minHeight, Size expected) { + Size verdict = CameraTooActivity.chooseBigEnoughSize(options, minWidth, minHeight); + assertEquals(String.format("Expected optimal size %s but got %s", expected, verdict), + verdict, expected); + } + + @Test + public void chooseBigEnoughSize() { + Size empty = new Size(0, 0), fatAndFlat = new Size(100, 0), tallAndThin = new Size(0, 100); + Size smallSquare = new Size(4, 4), horizRect = new Size(8, 2), vertRect = new Size(2, 8); + Size largeSquare = new Size(5, 5); + Size[] siz = + { empty, fatAndFlat, tallAndThin, smallSquare, horizRect, vertRect, largeSquare }; + + assertOptimalSize(siz, 0, 0, empty); + + assertOptimalSize(siz, 1, 0, fatAndFlat); + assertOptimalSize(siz, 0, 1, tallAndThin); + + assertOptimalSize(siz, 4, 4, smallSquare); + assertOptimalSize(siz, 1, 1, smallSquare); + assertOptimalSize(siz, 2, 1, smallSquare); + assertOptimalSize(siz, 1, 2, smallSquare); + assertOptimalSize(siz, 3, 4, smallSquare); + assertOptimalSize(siz, 4, 3, smallSquare); + + assertOptimalSize(siz, 8, 2, horizRect); + assertOptimalSize(siz, 5, 1, horizRect); + assertOptimalSize(siz, 5, 2, horizRect); + + assertOptimalSize(siz, 2, 8, vertRect); + assertOptimalSize(siz, 1, 5, vertRect); + assertOptimalSize(siz, 2, 5, vertRect); + + assertOptimalSize(siz, 5, 5, largeSquare); + assertOptimalSize(siz, 3, 5, largeSquare); + assertOptimalSize(siz, 5, 3, largeSquare); + } + + private static final FilenameFilter OUTPUT_FILE_DECIDER = new FilenameFilter() { + @Override + public boolean accept(File dir, String filename) { + return filename.indexOf("cameratoo") == 0 && + filename.indexOf(".jpg") == filename.length() - ".jpg".length(); + }}; + + private static <T> Set<T> newlyAddedElements(Set<T> before, Set<T> after) { + Set<T> result = new HashSet<T>(after); + result.removeAll(before); + return result; + } + + @Test + public void capturedImageSaver() throws FileNotFoundException, IOException { + ByteBuffer buf = ByteBuffer.allocate(25); + for(int index = 0; index < buf.capacity(); ++index) + buf.put(index, (byte) index); + + Image.Plane plane = mock(Image.Plane.class); + when(plane.getBuffer()).thenReturn(buf); + when(plane.getPixelStride()).thenReturn(1); + when(plane.getRowStride()).thenReturn(5); + + Image.Plane[] onlyPlaneThatMatters = { plane }; + Image image = mock(Image.class); + when(image.getPlanes()).thenReturn(onlyPlaneThatMatters); + when(image.getWidth()).thenReturn(5); + when(image.getHeight()).thenReturn(5); + + File picturesFolder = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + Set<File> preListing = + new HashSet<File>(Arrays.asList(picturesFolder.listFiles(OUTPUT_FILE_DECIDER))); + + CameraTooActivity.CapturedImageSaver saver = + new CameraTooActivity.CapturedImageSaver(image); + saver.run(); + + Set<File> postListing = + new HashSet<File>(Arrays.asList(picturesFolder.listFiles(OUTPUT_FILE_DECIDER))); + Set<File> newFiles = newlyAddedElements(preListing, postListing); + + assertEquals(newFiles.size(), 1); + + File picture = newFiles.iterator().next(); + FileInputStream istream = new FileInputStream(picture); + + for(int count = 0; count < buf.capacity(); ++count) { + assertEquals(istream.read(), buf.get(count)); + } + assertEquals(istream.read(), -1); + assertTrue(picture.delete()); + } +} diff --git a/tests/JobSchedulerTestApp/AndroidManifest.xml b/tests/JobSchedulerTestApp/AndroidManifest.xml index 7431737..9654197 100644 --- a/tests/JobSchedulerTestApp/AndroidManifest.xml +++ b/tests/JobSchedulerTestApp/AndroidManifest.xml @@ -25,6 +25,7 @@ <service android:name=".service.TestJobService" + android:permission="android.permission.BIND_JOB_SERVICE" android:exported="true"/> </application> diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java index 393c594..15050ef 100644 --- a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java +++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java @@ -17,8 +17,8 @@ package com.android.demo.jobSchedulerApp; import android.app.Activity; -import android.app.task.Task; -import android.app.task.TaskParams; +import android.app.job.JobInfo; +import android.app.job.JobParameters; import android.content.ComponentName; import android.content.Intent; import android.content.res.Resources; @@ -80,10 +80,10 @@ public class MainActivity extends Activity { RadioButton mWiFiConnectivityRadioButton; RadioButton mAnyConnectivityRadioButton; ComponentName mServiceComponent; - /** Service object to interact scheduled tasks. */ + /** Service object to interact scheduled jobs. */ TestJobService mTestService; - private static int kTaskId = 0; + private static int kJobId = 0; Handler mHandler = new Handler(/* default looper */) { @Override @@ -112,7 +112,7 @@ public class MainActivity extends Activity { } /** - * UI onclick listener to schedule a task. What this task is is defined in + * UI onclick listener to schedule a job. What this job is is defined in * TestJobService#scheduleJob() */ public void scheduleJob(View v) { @@ -120,7 +120,7 @@ public class MainActivity extends Activity { return; } - Task.Builder builder = new Task.Builder(kTaskId++, mServiceComponent); + JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent); String delay = mDelayEditText.getText().toString(); if (delay != null && !TextUtils.isEmpty(delay)) { @@ -133,9 +133,9 @@ public class MainActivity extends Activity { boolean requiresUnmetered = mWiFiConnectivityRadioButton.isSelected(); boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isSelected(); if (requiresUnmetered) { - builder.setRequiredNetworkCapabilities(Task.NetworkType.UNMETERED); + builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED); } else if (requiresAnyConnectivity) { - builder.setRequiredNetworkCapabilities(Task.NetworkType.ANY); + builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY); } mTestService.scheduleJob(builder.build()); @@ -143,24 +143,24 @@ public class MainActivity extends Activity { } /** - * UI onclick listener to call taskFinished() in our service. + * UI onclick listener to call jobFinished() in our service. */ public void finishJob(View v) { if (!ensureTestService()) { return; } - mTestService.callTaskFinished(); + mTestService.callJobFinished(); mParamsTextView.setText(""); } - public void onReceivedStartTask(TaskParams params) { + public void onReceivedStartJob(JobParameters params) { mShowStartView.setBackgroundColor(startJobColor); Message m = Message.obtain(mHandler, MSG_UNCOLOUR_START); mHandler.sendMessageDelayed(m, 1000L); // uncolour in 1 second. - mParamsTextView.setText("Executing: " + params.getTaskId() + " " + params.getExtras()); + mParamsTextView.setText("Executing: " + params.getJobId() + " " + params.getExtras()); } - public void onReceivedStopTask() { + public void onReceivedStopJob() { mShowStopView.setBackgroundColor(stopJobColor); Message m = Message.obtain(mHandler, MSG_UNCOLOUR_STOP); mHandler.sendMessageDelayed(m, 2000L); // uncolour in 1 second. diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java index 7dd3cf1..bf8e887 100644 --- a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java +++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java @@ -16,28 +16,20 @@ package com.android.demo.jobSchedulerApp.service; -import android.app.Service; -import android.app.task.Task; -import android.app.task.TaskManager; -import android.app.task.TaskParams; -import android.app.task.TaskService; -import android.content.ComponentName; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.app.job.JobParameters; +import android.app.job.JobService; import android.content.Context; import android.content.Intent; -import android.os.Binder; -import android.os.IBinder; import android.os.Message; import android.os.Messenger; -import android.os.PersistableBundle; import android.os.RemoteException; import android.util.Log; import com.android.demo.jobSchedulerApp.MainActivity; -import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedList; -import java.util.List; /** @@ -52,7 +44,7 @@ import java.util.List; * lifecycle of our and provide a handle to said SyncAdapter to the OS on * request. */ -public class TestJobService extends TaskService { +public class TestJobService extends JobService { private static final String TAG = "SyncService"; @Override @@ -82,44 +74,44 @@ public class TestJobService extends TaskService { } @Override - public boolean onStartTask(TaskParams params) { - taskParamsMap.add(params); + public boolean onStartJob(JobParameters params) { + jobParamsMap.add(params); if (mActivity != null) { - mActivity.onReceivedStartTask(params); + mActivity.onReceivedStartJob(params); } - Log.i(TAG, "on start task: " + params.getTaskId()); + Log.i(TAG, "on start job: " + params.getJobId()); return true; } @Override - public boolean onStopTask(TaskParams params) { - taskParamsMap.remove(params); - mActivity.onReceivedStopTask(); - Log.i(TAG, "on stop task: " + params.getTaskId()); + public boolean onStopJob(JobParameters params) { + jobParamsMap.remove(params); + mActivity.onReceivedStopJob(); + Log.i(TAG, "on stop job: " + params.getJobId()); return true; } MainActivity mActivity; - private final LinkedList<TaskParams> taskParamsMap = new LinkedList<TaskParams>(); + private final LinkedList<JobParameters> jobParamsMap = new LinkedList<JobParameters>(); public void setUiCallback(MainActivity activity) { mActivity = activity; } /** Send job to the JobScheduler. */ - public void scheduleJob(Task t) { + public void scheduleJob(JobInfo t) { Log.d(TAG, "Scheduling job"); - TaskManager tm = - (TaskManager) getSystemService(Context.TASK_SERVICE); + JobScheduler tm = + (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); tm.schedule(t); } - public boolean callTaskFinished() { - TaskParams params = taskParamsMap.poll(); + public boolean callJobFinished() { + JobParameters params = jobParamsMap.poll(); if (params == null) { return false; } else { - taskFinished(params, false); + jobFinished(params, false); return true; } } diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml index 113dce3..db2efc3 100644 --- a/tests/VectorDrawableTest/AndroidManifest.xml +++ b/tests/VectorDrawableTest/AndroidManifest.xml @@ -17,13 +17,13 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.test.dynamic" > + <uses-sdk android:minSdkVersion="20" /> <application android:hardwareAccelerated="true" android:label="vector" > - - <activity + <activity android:name="VectorDrawablePerformance" android:label="Vector Performance" > <intent-filter> @@ -31,13 +31,13 @@ <category android:name="com.android.test.dynamic.TEST" /> </intent-filter> - </activity> <activity android:name="VectorDrawableAnimation" android:label="VectorTestAnimation" > <intent-filter> <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.dynamic.TEST" /> </intent-filter> </activity> @@ -52,6 +52,15 @@ </intent-filter> </activity> <activity + android:name="AnimatedVectorDrawableTest" + android:label="AnimatedVectorDrawableTest" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="com.android.test.dynamic.TEST" /> + </intent-filter> + </activity> + <activity android:name="VectorDrawable01" android:label="VectorTest1" > <intent-filter> @@ -68,7 +77,6 @@ <category android:name="com.android.test.dynamic.TEST" /> </intent-filter> - </activity> <activity android:name="VectorDrawableStaticPerf" @@ -78,9 +86,7 @@ <category android:name="com.android.test.dynamic.TEST" /> </intent-filter> - </activity> - <activity android:name="VectorCheckbox" android:label="On a Checkbox" > @@ -89,7 +95,6 @@ <category android:name="com.android.test.dynamic.TEST" /> </intent-filter> - </activity> <activity android:name="VectorPathChecking" @@ -99,7 +104,6 @@ <category android:name="com.android.test.dynamic.TEST" /> </intent-filter> - </activity> </application> diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation01.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation01.xml new file mode 100644 index 0000000..d47e019 --- /dev/null +++ b/tests/VectorDrawableTest/res/anim/trim_path_animation01.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" > + + <set android:ordering="sequentially" > + <objectAnimator + android:duration="5000" + android:propertyName="trimPathEnd" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + <objectAnimator + android:duration="5000" + android:propertyName="trimPathEnd" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType" /> + </set> + +</set>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ticker_background.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation02.xml index 7320fa0..3bf2865 100644 --- a/packages/SystemUI/res/drawable/ticker_background.xml +++ b/tests/VectorDrawableTest/res/anim/trim_path_animation02.xml @@ -1,12 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2006 The Android Open Source Project +<!-- + Copyright (C) 2014 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. @@ -14,14 +15,12 @@ limitations under the License. --> -<layer-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:opacity="translucent" - > - <!-- the large icon extends 12dp beyond the edge of the status bar --> - <item - android:drawable="@drawable/notification_item_background_color" - android:top="12dp" - /> -</layer-list> +<set xmlns:android="http://schemas.android.com/apk/res/android" > + + <objectAnimator + android:duration="5000" + android:propertyName="fill" + android:valueFrom="#FF000000" + android:valueTo="#FFFF0000"/> +</set>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/notification_row_legacy_bg.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml index ce3372e..72beba2 100644 --- a/packages/SystemUI/res/drawable/notification_row_legacy_bg.xml +++ b/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project +<!-- + Copyright (C) 2014 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. @@ -14,9 +15,12 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android" - android:exitFadeDuration="@android:integer/config_mediumAnimTime"> +<set xmlns:android="http://schemas.android.com/apk/res/android" > - <item android:state_pressed="true" android:drawable="@drawable/notification_item_background_color_pressed" /> - <item android:state_pressed="false" android:drawable="@drawable/notification_item_background_legacy_color" /> -</selector> + <objectAnimator + android:duration="6000" + android:propertyName="rotation" + android:valueFrom="0" + android:valueTo="360"/> + +</set>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml new file mode 100644 index 0000000..ff86668 --- /dev/null +++ b/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android" > + + <objectAnimator + android:duration="9000" + android:propertyName="rotation" + android:valueFrom="0" + android:valueTo="360"/> + +</set>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml new file mode 100644 index 0000000..b8681b6 --- /dev/null +++ b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml @@ -0,0 +1,36 @@ +<!-- + Copyright (C) 2014 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. +--> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/vector_drawable12" > + + <target + android:name="pie1" + android:animation="@anim/trim_path_animation01" /> + <target + android:name="v" + android:animation="@anim/trim_path_animation02" /> + + <target + android:name="rotationGroup" + android:animation="@anim/trim_path_animation03" /> + <target + android:name="rotationGroup3" + android:animation="@anim/trim_path_animation03" /> + <target + android:name="rotationGroupBlue" + android:animation="@anim/trim_path_animation03" /> + +</animated-vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml index 3042f6a..e28ec41 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml @@ -23,11 +23,15 @@ android:viewportHeight="600" android:viewportWidth="600" /> - <group> + <group + android:name="rotationGroup" + android:pivotX="300.0" + android:pivotY="300.0" + android:rotation="45.0" > <path android:name="pie1" - android:pathData="M300,70 a230,230 0 1,0 1,0 z" android:fill="#00000000" + android:pathData="M300,70 a230,230 0 1,0 1,0 z" android:stroke="#FF00FF00" android:strokeWidth="70" android:trimPathEnd=".75" @@ -36,7 +40,66 @@ <path android:name="v" android:fill="#FF00FF00" - android:pathData="M300,70 l 0,-70 70,70 -70,70z"/> + android:pathData="M300,70 l 0,-70 70,70 -70,70z" /> + + <group + android:name="translateToCenterGroup" + android:rotation="0.0" + android:translateX="200.0" + android:translateY="200.0" > + <path + android:name="twoLines" + android:pathData="@string/twoLinePathData" + android:stroke="#FFFF0000" + android:strokeWidth="20" /> + + <group + android:name="rotationGroup2" + android:pivotX="0.0" + android:pivotY="0.0" + android:rotation="-45.0" > + <path + android:name="twoLines1" + android:pathData="@string/twoLinePathData" + android:stroke="#FF00FF00" + android:strokeWidth="20" /> + + <group + android:name="translateGroupHalf" + android:translateX="65.0" + android:translateY="80.0" > + <group + android:name="rotationGroup3" + android:pivotX="-65.0" + android:pivotY="-80.0" + android:rotation="-45.0" > + <path + android:name="twoLines2" + android:fill="#FF00FF00" + android:pathData="@string/twoLinePathData" + android:stroke="#FF00FF00" + android:strokeWidth="20" /> + + <group + android:name="translateGroup" + android:translateX="65.0" + android:translateY="80.0" > + <group + android:name="rotationGroupBlue" + android:pivotX="-65.0" + android:pivotY="-80.0" + android:rotation="-45.0" > + <path + android:name="twoLines3" + android:pathData="@string/twoLinePathData" + android:stroke="#FF0000FF" + android:strokeWidth="20" /> + </group> + </group> + </group> + </group> + </group> + </group> </group> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java new file mode 100644 index 0000000..6e864fa --- /dev/null +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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.test.dynamic; + +import android.app.Activity; +import android.graphics.drawable.AnimatedVectorDrawable; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; + +public class AnimatedVectorDrawableTest extends Activity { + private static final String LOGCAT = "VectorDrawableAnimationTest"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Button button = new Button(this); + button.setBackgroundResource(R.drawable.animation_vector_drawable01); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + AnimatedVectorDrawable frameAnimation = (AnimatedVectorDrawable) v.getBackground(); + frameAnimation.start(); + } + }); + + setContentView(button); + } +} diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java index 99de037..93b06b6 100644 --- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java @@ -14,6 +14,7 @@ package com.android.test.dynamic; +import android.animation.ValueAnimator; import android.app.Activity; import android.graphics.drawable.AnimationDrawable; import android.os.Bundle; @@ -27,7 +28,7 @@ public class VectorDrawableAnimation extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Button button = new Button(this); + final Button button = new Button(this); button.setBackgroundResource(R.drawable.animation_drawable_vector); button.setOnClickListener(new View.OnClickListener() { diff --git a/tests/VoiceEnrollment/Android.mk b/tests/VoiceEnrollment/Android.mk new file mode 100644 index 0000000..2ab3d02 --- /dev/null +++ b/tests/VoiceEnrollment/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := VoiceEnrollment + +LOCAL_MODULE_TAGS := optional + +LOCAL_PRIVILEGED_MODULE := true + +include $(BUILD_PACKAGE) diff --git a/tests/VoiceEnrollment/AndroidManifest.xml b/tests/VoiceEnrollment/AndroidManifest.xml new file mode 100644 index 0000000..6321222 --- /dev/null +++ b/tests/VoiceEnrollment/AndroidManifest.xml @@ -0,0 +1,16 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.voiceenrollment"> + + <application + android:permission="android.permission.MANAGE_VOICE_KEYPHRASES"> + <activity android:name="TestEnrollmentActivity" android:label="Voice Enrollment Application" + android:theme="@android:style/Theme.Material.Light.Voice"> + <intent-filter> + <action android:name="com.android.intent.action.MANAGE_VOICE_KEYPHRASES" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + <meta-data android:name="android.voice_enrollment" + android:resource="@xml/enrollment_application"/> + </application> +</manifest> diff --git a/tests/VoiceEnrollment/res/xml/enrollment_application.xml b/tests/VoiceEnrollment/res/xml/enrollment_application.xml new file mode 100644 index 0000000..710a0ac --- /dev/null +++ b/tests/VoiceEnrollment/res/xml/enrollment_application.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (c) 2014, 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. + */ +--> + +<voice-enrollment-application xmlns:android="http://schemas.android.com/apk/res/android" + android:searchKeyphraseId="101" + android:searchKeyphrase="Hello There" + android:searchKeyphraseSupportedLocales="en-US,en-GB,fr-FR,de-DE" /> diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java new file mode 100644 index 0000000..7fbd965 --- /dev/null +++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 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.test.voiceenrollment; + +import android.app.Activity; + +public class TestEnrollmentActivity extends Activity { + // TODO(sansid): Add a test enrollment flow here. +} diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java index d40b05f..00c2c64 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java @@ -21,6 +21,8 @@ import android.os.Bundle; import android.service.voice.VoiceInteractionService; import android.util.Log; +import java.util.Arrays; + public class MainInteractionService extends VoiceInteractionService { static final String TAG = "MainInteractionService"; @@ -28,6 +30,9 @@ public class MainInteractionService extends VoiceInteractionService { public void onCreate() { super.onCreate(); Log.i(TAG, "Creating " + this); + Log.i(TAG, "Keyphrase enrollment error? " + getKeyphraseEnrollmentInfo().getParseError()); + Log.i(TAG, "Keyphrase enrollment meta-data: " + + Arrays.toString(getKeyphraseEnrollmentInfo().getKeyphrases())); } @Override diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index af22f44..36102f1 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -151,7 +151,7 @@ public final class BridgeInflater extends LayoutInflater { @Override public View inflate(int resource, ViewGroup root) { Context context = getContext(); - if (context instanceof ContextThemeWrapper) { + while (context instanceof ContextThemeWrapper) { context = ((ContextThemeWrapper) context).getBaseContext(); } if (context instanceof BridgeContext) { @@ -217,7 +217,7 @@ public final class BridgeInflater extends LayoutInflater { private void setupViewInContext(View view, AttributeSet attrs) { Context context = getContext(); - if (context instanceof ContextThemeWrapper) { + while (context instanceof ContextThemeWrapper) { context = ((ContextThemeWrapper) context).getBaseContext(); } if (context instanceof BridgeContext) { diff --git a/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java index 0dddf3d..dafc96b 100644 --- a/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java @@ -48,7 +48,7 @@ public class MenuInflater_Delegate { AttributeSet attrs) { if (menuItem instanceof BridgeMenuItemImpl) { Context context = thisInflater.getContext(); - if (context instanceof ContextThemeWrapper) { + while (context instanceof ContextThemeWrapper) { context = ((ContextThemeWrapper) context).getBaseContext(); } if (context instanceof BridgeContext) { diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java b/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java index cdb839a..8d1d0c1 100644 --- a/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java +++ b/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java @@ -42,7 +42,7 @@ public class BridgeMenuItemImpl extends MenuItemImpl { CharSequence title, int showAsAction) { super(menu, group, id, categoryOrder, ordering, title, showAsAction); Context context = menu.getContext(); - if (context instanceof ContextThemeWrapper) { + while (context instanceof ContextThemeWrapper) { context = ((ContextThemeWrapper) context).getBaseContext(); } if (context instanceof BridgeContext) { diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt index 6e0a300..32625ae 100644 --- a/tools/layoutlib/create/README.txt +++ b/tools/layoutlib/create/README.txt @@ -119,8 +119,8 @@ name. The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but updates the references in all classes. This is used to update the references of classes in the java package that -were added in the Dalvik VM but are not a part of the standard JVM. The existing classes are -modified to update all references to these non-standard classes. An alternate implementation of +were added in the Dalvik VM but are not a part of the Desktop VM. The existing classes are +modified to update all references to these non-desktop classes. An alternate implementation of these (com.android.tools.layoutlib.java.*) is injected. RenameClassAdapter and RefactorClassAdapter both inherit from AbstractClassAdapter which changes the @@ -130,11 +130,15 @@ the StackMapTable correctly and Java 7 VM enforces that classes with version gre valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on Mac has horrible font rendering support. +ReplaceMethodCallsAdapter replaces calls to certain methods. Currently, it only rewrites calls to +java.lang.System.arraycopy([CI[CII)V, which is not part of the Desktop VM to call the more general +method java.lang.System.arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V. + The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7 Transformation chains in the asm user guide, link in the References.) The order of execution of these is: ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] -> -RefactorClassAdapter -> ClassWriter +RefactorClassAdapter -> [ReplaceMethodCallsAdapter] -> ClassWriter - Method stubs -------------- @@ -169,7 +173,7 @@ This is the easiest: we currently inject the following classes: - AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new classes are injected. The implementation for these classes has been taken from Android's libcore (platform/libcore/luni/src/main/java/java/...). -- Charsets, IntegralToString and UnsafeByteSequence are not part of the standard JAVA VM. They are +- Charsets, IntegralToString and UnsafeByteSequence are not part of the Desktop VM. They are added to the Dalvik VM for performance reasons. An implementation that is very close to the original (which is at platform/libcore/luni/src/main/java/...) is injected. Since these classees were in part of the java package, where we can't inject classes, all references to these have been @@ -209,7 +213,7 @@ This won't rename/replace the inner static methods of a given class. This is very similar to the Renaming classes except that it also updates the reference in all classes. This is done for classes which are added to the Dalvik VM for performance reasons but are -not present in the Standard Java VM. An implementation for these classes is also injected. +not present in the Desktop VM. An implementation for these classes is also injected. 5- Method erasure based on return type diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java index 3e75c9e..8373e30 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java @@ -32,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -63,7 +64,8 @@ public class AsmAnalyzer { private final Set<String> mExcludedClasses; /** Glob patterns of files to keep as is. */ private final String[] mIncludeFileGlobs; - /** Copy these files into the output as is. */ + /** Internal names of classes that contain method calls that need to be rewritten. */ + private final Set<String> mReplaceMethodCallClasses = new HashSet<String>(); /** * Creates a new analyzer. @@ -109,6 +111,7 @@ public class AsmAnalyzer { mGen.setKeep(found); mGen.setDeps(deps); mGen.setCopyFiles(filesFound); + mGen.setRewriteMethodCallClasses(mReplaceMethodCallClasses); } } @@ -118,7 +121,7 @@ public class AsmAnalyzer { * * @param classes The map of class name => ASM ClassReader. Class names are * in the form "android.view.View". - * @param fileFound The map of file name => InputStream. The file name is + * @param filesFound The map of file name => InputStream. The file name is * in the form "android/data/dataFile". */ void parseZip(List<String> jarPathList, Map<String, ClassReader> classes, @@ -143,8 +146,8 @@ public class AsmAnalyzer { String className = classReaderToClassName(cr); classes.put(className, cr); } else { - for (int i = 0; i < includeFilePatterns.length; ++i) { - if (includeFilePatterns[i].matcher(entry.getName()).matches()) { + for (Pattern includeFilePattern : includeFilePatterns) { + if (includeFilePattern.matcher(entry.getName()).matches()) { filesFound.put(entry.getName(), zip.getInputStream(entry)); break; } @@ -321,6 +324,7 @@ public class AsmAnalyzer { deps, new_deps); for (ClassReader cr : inOutKeepClasses.values()) { + visitor.setClassName(cr.getClassName()); cr.accept(visitor, 0 /* flags */); } @@ -367,6 +371,8 @@ public class AsmAnalyzer { /** New classes to keep as-is found by this visitor. */ private final Map<String, ClassReader> mOutKeep; + private String mClassName; + /** * Creates a new visitor that will find all the dependencies for the visited class. * Types which are already in the zipClasses, keepClasses or inDeps are not marked. @@ -390,6 +396,10 @@ public class AsmAnalyzer { mOutDeps = outDeps; } + private void setClassName(String className) { + mClassName = className; + } + /** * Considers the given class name as a dependency. * If it does, add to the mOutDeps map. @@ -429,7 +439,7 @@ public class AsmAnalyzer { // - android classes are added to dependencies // - non-android classes are added to the list of classes to keep as-is (they don't need // to be stubbed). - if (className.indexOf("android") >= 0) { // TODO make configurable + if (className.contains("android")) { // TODO make configurable mOutDeps.put(className, cr); } else { mOutKeep.put(className, cr); @@ -594,7 +604,7 @@ public class AsmAnalyzer { // type and exceptions do not use generic types. considerSignature(signature); - return new MyMethodVisitor(); + return new MyMethodVisitor(mClassName); } @Override @@ -614,8 +624,11 @@ public class AsmAnalyzer { private class MyMethodVisitor extends MethodVisitor { - public MyMethodVisitor() { + private String mOwnerClass; + + public MyMethodVisitor(String ownerClass) { super(Opcodes.ASM4); + mOwnerClass = ownerClass; } @@ -709,6 +722,13 @@ public class AsmAnalyzer { considerName(owner); // desc is the method's descriptor (see Type). considerDesc(desc); + + + // Check if method is java.lang.System.arrayCopy([CI[CII)V + if (owner.equals("java/lang/System") && name.equals("arraycopy") + && desc.equals("([CI[CII)V")) { + mReplaceMethodCallClasses.add(mOwnerClass); + } } // instruction multianewarray, whatever that is diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index 207d8ae..c96a143 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -21,7 +21,6 @@ import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -55,6 +54,8 @@ public class AsmGenerator { private Map<String, ClassReader> mDeps; /** All files that are to be copied as-is. */ private Map<String, InputStream> mCopyFiles; + /** All classes where certain method calls need to be rewritten. */ + private Set<String> mReplaceMethodCallsClasses; /** Counter of number of classes renamed during transform. */ private int mRenameCount; /** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */ @@ -133,7 +134,7 @@ public class AsmGenerator { assert i + 1 < n; String oldFqcn = binaryToInternalClassName(refactorClasses[i]); String newFqcn = binaryToInternalClassName(refactorClasses[i + 1]); - mRefactorClasses.put(oldFqcn, newFqcn);; + mRefactorClasses.put(oldFqcn, newFqcn); } // create the map of renamed class -> return type of method to delete. @@ -203,23 +204,12 @@ public class AsmGenerator { mCopyFiles = copyFiles; } - /** Gets the map of classes to output as-is, except if they have native methods */ - public Map<String, ClassReader> getKeep() { - return mKeep; - } - - /** Gets the map of dependencies that must be completely stubbed */ - public Map<String, ClassReader> getDeps() { - return mDeps; - } - - /** Gets the map of files to output as-is. */ - public Map<String, InputStream> getCopyFiles() { - return mCopyFiles; + public void setRewriteMethodCallClasses(Set<String> rewriteMethodCallClasses) { + mReplaceMethodCallsClasses = rewriteMethodCallClasses; } /** Generates the final JAR */ - public void generate() throws FileNotFoundException, IOException { + public void generate() throws IOException { TreeMap<String, byte[]> all = new TreeMap<String, byte[]>(); for (Class<?> clazz : mInjectClasses) { @@ -329,14 +319,14 @@ public class AsmGenerator { String newName = transformName(className); // transformName returns its input argument if there's no need to rename the class - if (newName != className) { + if (!newName.equals(className)) { mRenameCount++; // This class is being renamed, so remove it from the list of classes not renamed. mClassesNotRenamed.remove(className); } mLog.debug("Transform %s%s%s%s", className, - newName == className ? "" : " (renamed to " + newName + ")", + newName.equals(className) ? "" : " (renamed to " + newName + ")", hasNativeMethods ? " -- has natives" : "", stubNativesOnly ? " -- stub natives only" : ""); @@ -344,8 +334,14 @@ public class AsmGenerator { // original class reader. ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); - ClassVisitor cv = new RefactorClassAdapter(cw, mRefactorClasses); - if (newName != className) { + ClassVisitor cv = cw; + + if (mReplaceMethodCallsClasses.contains(className)) { + cv = new ReplaceMethodCallsAdapter(cv); + } + + cv = new RefactorClassAdapter(cv, mRefactorClasses); + if (!newName.equals(className)) { cv = new RenameClassAdapter(cv, className, newName); } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 2e952fc..ad10656 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -193,8 +193,7 @@ public class Main { private static boolean processArgs(Log log, String[] args, ArrayList<String> osJarPath, String[] osDestJar) { boolean needs_dest = true; - for (int i = 0; i < args.length; i++) { - String s = args[i]; + for (String s : args) { if (s.equals("-v")) { log.setVerbose(true); } else if (s.equals("-p")) { @@ -212,7 +211,7 @@ public class Main { osJarPath.add(s); } } else { - log.error("Unknow argument: %s", s); + log.error("Unknown argument: %s", s); return false; } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java new file mode 100644 index 0000000..e57eba1 --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 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.tools.layoutlib.create; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * Replaces calls to certain methods that do not exist in the Desktop VM. + */ +public class ReplaceMethodCallsAdapter extends ClassVisitor { + public ReplaceMethodCallsAdapter(ClassVisitor cv) { + super(Opcodes.ASM4, cv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { + return new MyMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions)); + } + + private class MyMethodVisitor extends MethodVisitor { + + public MyMethodVisitor(MethodVisitor mv) { + super(Opcodes.ASM4, mv); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc) { + // Check if method is java.lang.System.arrayCopy([CI[CII)V + if (owner.equals("java/lang/System") && name.equals("arraycopy") + && desc.equals("([CI[CII)V")) { + desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V"; + } + super.visitMethodInsn(opcode, owner, name, desc); + } + } +} diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py index aea3241..bd9b14c 100755 --- a/tools/layoutlib/rename_font/build_font.py +++ b/tools/layoutlib/rename_font/build_font.py @@ -27,7 +27,7 @@ import sys from fontTools import ttx import re import os -from lxml import etree +import xml.etree.ElementTree as etree import shutil import glob from multiprocessing import Pool @@ -84,14 +84,13 @@ def convert_font(input_path): ttx.main(ttx_args) # now parse the xml file to change its PS name. tree = etree.parse(ttx_path) - encoding = tree.docinfo.encoding root = tree.getroot() for name in root.iter('name'): [old_ps_name, version] = get_font_info(name) if old_ps_name is not None and version is not None: new_ps_name = old_ps_name + version update_name(name, new_ps_name) - tree.write(ttx_path, xml_declaration=True, encoding=encoding ) + tree.write(ttx_path, xml_declaration=True, encoding='utf-8' ) # generate the udpated font now. ttx_args = ['-q', '-d', dest_dir, ttx_path] ttx.main(ttx_args) diff --git a/tools/layoutlib/rename_font/test.py b/tools/layoutlib/rename_font/test.py index d4c86cb..b0b69d8 100755 --- a/tools/layoutlib/rename_font/test.py +++ b/tools/layoutlib/rename_font/test.py @@ -14,7 +14,7 @@ import build_font from fontTools import ttx import os -from lxml import etree +import xml.etree.ElementTree as etree import shutil import tempfile diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 99151c3..8191edd 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -89,6 +89,21 @@ public class ScanResult implements Parcelable { * {@hide} */ public final static int UNSPECIFIED = -1; + /** + * @hide + * TODO: makes real freq boundaries + */ + public boolean is24GHz() { + return frequency > 2400 && frequency < 2500; + } + + /** + * @hide + * TODO: makes real freq boundaries + */ + public boolean is5GHz() { + return frequency > 4900 && frequency < 5900; + } /** information element from beacon * @hide diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 192cba6..48396d5 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -405,6 +405,10 @@ public class WifiConfiguration implements Parcelable { /** @hide **/ public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_5 = -70; + /** @hide + * 5GHz band is prefered over 2.4 if the 5GHz RSSI is higher than this threshold **/ + public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65; + /** * @hide * A summary of the RSSI and Band status for that configuration @@ -481,11 +485,11 @@ public class WifiConfiguration implements Parcelable { if (result.seen == 0) continue; - if ((result.frequency > 4900) && (result.frequency < 5900)) { + if (result.is5GHz()) { //strictly speaking: [4915, 5825] //number of known BSSID on 5GHz band status.num5 = status.num5 + 1; - } else if ((result.frequency > 2400) && (result.frequency < 2500)) { + } else if (result.is24GHz()) { //strictly speaking: [2412, 2482] //number of known BSSID on 2.4Ghz band status.num24 = status.num24 + 1; @@ -493,12 +497,12 @@ public class WifiConfiguration implements Parcelable { if ((now_ms - result.seen) > age) continue; - if ((result.frequency > 4900) && (result.frequency < 5900)) { + if (result.is5GHz()) { if (result.level > status.rssi5) { status.rssi5 = result.level; status.age5 = result.seen; } - } else if ((result.frequency > 2400) && (result.frequency < 2500)) { + } else if (result.is24GHz()) { if (result.level > status.rssi24) { status.rssi24 = result.level; status.age24 = result.seen; @@ -547,6 +551,17 @@ public class WifiConfiguration implements Parcelable { */ public long blackListTimestamp; + /** + * @hide + * last time the system was connected to this configuration. + */ + public long lastConnected; + + /** + * @hide + * last time the system was disconnected to this configuration. + */ + public long lastDisconnected; /** * Set if the configuration was self added by the framework @@ -658,7 +673,20 @@ public class WifiConfiguration implements Parcelable { // TODO: Add more checks return true; + } + /** + * Helper function, identify if a configuration is linked + * @hide + */ + public boolean isLinked(WifiConfiguration config) { + if (config.linkedConfigurations != null && linkedConfigurations != null) { + if (config.linkedConfigurations.get(configKey()) != null + && linkedConfigurations.get(config.configKey()) != null) { + return true; + } + } + return false; } /** @@ -688,6 +716,7 @@ public class WifiConfiguration implements Parcelable { /** @hide **/ public void setAutoJoinStatus(int status) { + if (status < 0) status = 0; if (status == 0) { blackListTimestamp = 0; } else if (status > autoJoinStatus) { @@ -1079,6 +1108,8 @@ public class WifiConfiguration implements Parcelable { creatorUid = source.creatorUid; peerWifiConfiguration = source.peerWifiConfiguration; blackListTimestamp = source.blackListTimestamp; + lastConnected = source.lastConnected; + lastDisconnected = source.lastDisconnected; } } diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index f6a94d0..6760c56 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -62,6 +62,17 @@ public class WifiInfo implements Parcelable { private String mBSSID; private WifiSsid mWifiSsid; private int mNetworkId; + + /** @hide **/ + public static final int INVALID_RSSI = -127; + + /** @hide **/ + public static final int MIN_RSSI = -126; + + /** @hide **/ + public static final int MAX_RSSI = 200; + + /** * Received Signal Strength Indicator */ @@ -131,7 +142,8 @@ public class WifiInfo implements Parcelable { public int score; /** - * @hide * + * TODO: get actual timestamp and calculate true rates + * @hide */ public void updatePacketRates(WifiLinkLayerStats stats) { if (stats != null) { @@ -156,17 +168,42 @@ public class WifiInfo implements Parcelable { rxSuccess = rxgood; txRetries = txretries; } else { - txBadRate = 0; + txBad = 0; txSuccess = 0; rxSuccess = 0; txRetries = 0; + txBadRate = 0; + txSuccessRate = 0; + rxSuccessRate = 0; + txRetriesRate = 0; } } + /** - * Flag indicating that AP has hinted that upstream connection is metered, - * and sensitive to heavy data transfers. + * This function is less powerful and used if the WifiLinkLayerStats API is not implemented + * at the Wifi HAL + * @hide */ + public void updatePacketRates(long txPackets, long rxPackets) { + //paranoia + txBad = 0; + txRetries = 0; + txBadRate = 0; + txRetriesRate = 0; + + txSuccessRate = (txSuccessRate * 0.5) + + ((double) (txPackets - txSuccess) * 0.5); + rxSuccessRate = (rxSuccessRate * 0.5) + + ((double) (rxPackets - rxSuccess) * 0.5); + txSuccess = txPackets; + rxSuccess = rxPackets; + } + + /** + * Flag indicating that AP has hinted that upstream connection is metered, + * and sensitive to heavy data transfers. + */ private boolean mMeteredHint; /** @hide */ @@ -175,9 +212,32 @@ public class WifiInfo implements Parcelable { mBSSID = null; mNetworkId = -1; mSupplicantState = SupplicantState.UNINITIALIZED; - mRssi = -9999; + mRssi = INVALID_RSSI; mLinkSpeed = -1; mFrequency = -1; + txBad = 0; + } + + /** @hide */ + public void reset() { + setInetAddress(null); + setBSSID(null); + setSSID(null); + setNetworkId(-1); + setRssi(INVALID_RSSI); + setLinkSpeed(-1); + setFrequency(-1); + setMeteredHint(false); + txSuccess = 0; + rxSuccess = 0; + txRetries = 0; + txBadRate = 0; + txSuccessRate = 0; + rxSuccessRate = 0; + txRetriesRate = 0; + lowRssiCount = 0; + badRssiCount = 0; + score = 0; } /** @@ -256,7 +316,7 @@ public class WifiInfo implements Parcelable { /** * Returns the received signal strength indicator of the current 802.11 * network, in dBm. - * @return the RSSI, in the range -110 to 10 + * @return the RSSI, in the range -127 to 200 */ public int getRssi() { return mRssi; @@ -264,6 +324,10 @@ public class WifiInfo implements Parcelable { /** @hide */ public void setRssi(int rssi) { + if (rssi < INVALID_RSSI) + rssi = INVALID_RSSI; + if (rssi > MAX_RSSI) + rssi = MAX_RSSI; mRssi = rssi; } diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 3b65ca8..21b700d 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -153,12 +153,17 @@ public class WifiScanner { dest.writeInt(band); dest.writeInt(periodInMs); dest.writeInt(reportEvents); - dest.writeInt(channels.length); - for (int i = 0; i < channels.length; i++) { - dest.writeInt(channels[i].frequency); - dest.writeInt(channels[i].dwellTimeMS); - dest.writeInt(channels[i].passive ? 1 : 0); + if (channels != null) { + dest.writeInt(channels.length); + + for (int i = 0; i < channels.length; i++) { + dest.writeInt(channels[i].frequency); + dest.writeInt(channels[i].dwellTimeMS); + dest.writeInt(channels[i].passive ? 1 : 0); + } + } else { + dest.writeInt(0); } } @@ -211,10 +216,14 @@ public class WifiScanner { /** Implement the Parcelable interface {@hide} */ public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mResults.length); - for (int i = 0; i < mResults.length; i++) { - ScanResult result = mResults[i]; - result.writeToParcel(dest, flags); + if (mResults != null) { + dest.writeInt(mResults.length); + for (int i = 0; i < mResults.length; i++) { + ScanResult result = mResults[i]; + result.writeToParcel(dest, flags); + } + } else { + dest.writeInt(0); } } @@ -324,13 +333,17 @@ public class WifiScanner { dest.writeInt(unchangedSampleSize); dest.writeInt(minApsBreachingThreshold); dest.writeInt(periodInMs); - dest.writeInt(hotspotInfos.length); - for (int i = 0; i < hotspotInfos.length; i++) { - HotspotInfo info = hotspotInfos[i]; - dest.writeString(info.bssid); - dest.writeInt(info.low); - dest.writeInt(info.high); - dest.writeInt(info.frequencyHint); + if (hotspotInfos != null) { + dest.writeInt(hotspotInfos.length); + for (int i = 0; i < hotspotInfos.length; i++) { + HotspotInfo info = hotspotInfos[i]; + dest.writeString(info.bssid); + dest.writeInt(info.low); + dest.writeInt(info.high); + dest.writeInt(info.frequencyHint); + } + } else { + dest.writeInt(0); } } @@ -456,13 +469,18 @@ public class WifiScanner { /** Implement the Parcelable interface {@hide} */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(apLostThreshold); - dest.writeInt(hotspotInfos.length); - for (int i = 0; i < hotspotInfos.length; i++) { - HotspotInfo info = hotspotInfos[i]; - dest.writeString(info.bssid); - dest.writeInt(info.low); - dest.writeInt(info.high); - dest.writeInt(info.frequencyHint); + + if (hotspotInfos != null) { + dest.writeInt(hotspotInfos.length); + for (int i = 0; i < hotspotInfos.length; i++) { + HotspotInfo info = hotspotInfos[i]; + dest.writeString(info.bssid); + dest.writeInt(info.low); + dest.writeInt(info.high); + dest.writeInt(info.frequencyHint); + } + } else { + dest.writeInt(0); } } @@ -680,6 +698,42 @@ public class WifiScanner { } } + /** @hide */ + public static class OperationResult implements Parcelable { + public int reason; + public String description; + + public OperationResult(int reason, String description) { + this.reason = reason; + this.description = description; + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(reason); + dest.writeString(description); + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<OperationResult> CREATOR = + new Creator<OperationResult>() { + public OperationResult createFromParcel(Parcel in) { + int reason = in.readInt(); + String description = in.readString(); + return new OperationResult(reason, description); + } + + public OperationResult[] newArray(int size) { + return new OperationResult[size]; + } + }; + } + private static class ServiceHandler extends Handler { ServiceHandler(Looper looper) { super(looper); @@ -717,9 +771,11 @@ public class WifiScanner { case CMD_OP_SUCCEEDED : ((ActionListener) listener).onSuccess(); break; - case CMD_OP_FAILED : - ((ActionListener) listener).onFailure(msg.arg1, (String)msg.obj); - removeListener(msg.arg2); + case CMD_OP_FAILED : { + OperationResult result = (OperationResult)msg.obj; + ((ActionListener) listener).onFailure(result.reason, result.description); + removeListener(msg.arg2); + } break; case CMD_SCAN_RESULT : ((ScanListener) listener).onResults( diff --git a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl index 8375d09..61c2b8a 100644 --- a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl +++ b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl @@ -16,6 +16,8 @@ package android.net.wifi.passpoint; +import android.net.wifi.ScanResult; +import android.net.wifi.passpoint.WifiPasspointPolicy; import android.os.Messenger; /** @@ -27,5 +29,6 @@ interface IWifiPasspointManager { Messenger getMessenger(); int getPasspointState(); + List<WifiPasspointPolicy> requestCredentialMatch(in List<ScanResult> requested); } diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java index 8ab5c1e..aec87976 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java @@ -286,10 +286,11 @@ public class WifiPasspointInfo implements Parcelable { public String toString() { StringBuffer sb = new StringBuffer(); - sb.append("BSSID: ").append(bssid); + sb.append("BSSID: ").append("(").append(bssid).append(")"); if (venueName != null) - sb.append(" venueName: ").append(venueName.replace("\n", "\\n")); + sb.append(" venueName: ").append("(") + .append(venueName.replace("\n", "\\n")).append(")"); if (networkAuthType != null) { sb.append(" networkAuthType: "); diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java index 55acbad..2f158c2 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java @@ -22,6 +22,8 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; @@ -45,58 +47,53 @@ public class WifiPasspointManager { /* Passpoint states values */ - /** Passpoint is in an known state. This should only occur in boot time @hide */ + /** Passpoint is in an unknown state. This should only occur in boot time */ public static final int PASSPOINT_STATE_UNKNOWN = 0; - /** Passpoint is disabled. This occurs when wifi is disabled. @hide */ + /** Passpoint is disabled. This occurs when wifi is disabled */ public static final int PASSPOINT_STATE_DISABLED = 1; - /** Passpoint is enabled and in discovery state. @hide */ + /** Passpoint is enabled and in discovery state */ public static final int PASSPOINT_STATE_DISCOVERY = 2; - /** Passpoint is enabled and in access state. @hide */ + /** Passpoint is enabled and in access state */ public static final int PASSPOINT_STATE_ACCESS = 3; - /** Passpoint is enabled and in provisioning state. @hide */ + /** Passpoint is enabled and in provisioning state */ public static final int PASSPOINT_STATE_PROVISION = 4; /* Passpoint callback error codes */ - /** Indicates that the operation failed due to an internal error @hide */ - public static final int ERROR = 0; + /** Indicates that the operation failed due to an internal error */ + public static final int REASON_ERROR = 0; - /** Indicates that the operation failed because wifi is disabled @hide */ - public static final int WIFI_DISABLED = 1; + /** Indicates that the operation failed because wifi is disabled */ + public static final int REASON_WIFI_DISABLED = 1; - /** Indicates that the operation failed because the framework is busy @hide */ - public static final int BUSY = 2; + /** Indicates that the operation failed because the framework is busy */ + public static final int REASON_BUSY = 2; + + /** Indicates that the operation failed because parameter is invalid */ + public static final int REASON_INVALID_PARAMETER = 3; + + /** Indicates that the operation failed because the server is not trusted */ + public static final int REASON_NOT_TRUSTED = 4; /** * protocol supported for Passpoint - * @hide */ public static final String PROTOCOL_DM = "OMA-DM-ClientInitiated"; /** * protocol supported for Passpoint - * @hide */ public static final String PROTOCOL_SOAP = "SPP-ClientInitiated"; /* Passpoint broadcasts */ /** - * Broadcast intent action indicating that Passpoint online sign up is - * avaiable. - * @hide - */ - public static final String PASSPOINT_OSU_AVAILABLE = - "android.net.wifi.passpoint.OSU_AVAILABLE"; - - /** * Broadcast intent action indicating that the state of Passpoint * connectivity has changed - * @hide */ public static final String PASSPOINT_STATE_CHANGED_ACTION = "android.net.wifi.passpoint.STATE_CHANGE"; @@ -104,7 +101,6 @@ public class WifiPasspointManager { /** * Broadcast intent action indicating that the saved Passpoint credential * list has changed - * @hide */ public static final String PASSPOINT_CRED_CHANGED_ACTION = "android.net.wifi.passpoint.CRED_CHANGE"; @@ -112,21 +108,18 @@ public class WifiPasspointManager { /** * Broadcast intent action indicating that Passpoint online sign up is * avaiable. - * @hide */ public static final String PASSPOINT_OSU_AVAILABLE_ACTION = "android.net.wifi.passpoint.OSU_AVAILABLE"; /** * Broadcast intent action indicating that user remediation is required - * @hide */ public static final String PASSPOINT_USER_REM_REQ_ACTION = "android.net.wifi.passpoint.USER_REM_REQ"; /** * Interface for callback invocation when framework channel is lost - * @hide */ public interface ChannelListener { /** @@ -138,14 +131,13 @@ public class WifiPasspointManager { /** * Interface for callback invocation on an application action - * @hide */ public interface ActionListener { /** The operation succeeded */ public void onSuccess(); /** - * * The operation failed + * The operation failed * * @param reason The reason for failure could be one of * {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY} @@ -155,7 +147,6 @@ public class WifiPasspointManager { /** * Interface for callback invocation when doing OSU or user remediation - * @hide */ public interface OsuRemListener { /** The operation succeeded */ @@ -171,11 +162,11 @@ public class WifiPasspointManager { /** * Browser launch is requried for user interaction. When this callback - * is called, app should launch browser / webview to the given URL. + * is called, app should launch browser / webview to the given URI. * - * @param url URL for browser launch + * @param uri URI for browser launch */ - public void onBrowserLaunch(String url); + public void onBrowserLaunch(String uri); /** * When this is called, app should dismiss the previously lanched browser. @@ -187,7 +178,6 @@ public class WifiPasspointManager { * A channel that connects the application to the wifi passpoint framework. * Most passpoint operations require a Channel as an argument. * An instance of Channel is obtained by doing a call on {@link #initialize} - * @hide */ public static class Channel { private final static int INVALID_LISTENER_KEY = 0; @@ -288,7 +278,8 @@ public class WifiPasspointManager { @Override public void handleMessage(Message message) { - Object listener = getListener(message.arg2, false); + Object listener = null; + switch (message.what) { case AsyncChannel.CMD_CHANNEL_DISCONNECTED: if (mChannelListener != null) { @@ -300,6 +291,7 @@ public class WifiPasspointManager { case REQUEST_ANQP_INFO_SUCCEEDED: WifiPasspointInfo result = (WifiPasspointInfo) message.obj; anqpRequestFinish(result); + listener = getListener(message.arg2, false); if (listener != null) { ((ActionListener) listener).onSuccess(); } @@ -307,6 +299,7 @@ public class WifiPasspointManager { case REQUEST_ANQP_INFO_FAILED: anqpRequestFinish((ScanResult) message.obj); + listener = getListener(message.arg2, false); if (listener == null) getListener(message.arg2, true); if (listener != null) { @@ -314,6 +307,31 @@ public class WifiPasspointManager { } break; + case START_OSU_SUCCEEDED: + listener = getListener(message.arg2, true); + if (listener != null) { + ((OsuRemListener) listener).onSuccess(); + } + break; + + case START_OSU_FAILED: + listener = getListener(message.arg2, true); + if (listener != null) { + ((OsuRemListener) listener).onFailure(message.arg1); + } + break; + + case START_OSU_BROWSER: + listener = getListener(message.arg2, true); + if (listener != null) { + ParcelableString str = (ParcelableString) message.obj; + if (str.string == null) + ((OsuRemListener) listener).onBrowserDismiss(); + else + ((OsuRemListener) listener).onBrowserLaunch(str.string); + } + break; + default: Log.d(TAG, "Ignored " + message); break; @@ -323,25 +341,46 @@ public class WifiPasspointManager { } - private static final int BASE = Protocol.BASE_WIFI_PASSPOINT_MANAGER; - - /** @hide */ - public static final int REQUEST_ANQP_INFO = BASE + 1; + public static class ParcelableString implements Parcelable { + public String string; - /** @hide */ - public static final int REQUEST_ANQP_INFO_FAILED = BASE + 2; + @Override + public int describeContents() { + return 0; + } - /** @hide */ - public static final int REQUEST_ANQP_INFO_SUCCEEDED = BASE + 3; + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(string); + } - /** @hide */ - public static final int REQUEST_OSU_INFO = BASE + 4; + public static final Parcelable.Creator<ParcelableString> CREATOR = + new Parcelable.Creator<ParcelableString>() { + @Override + public ParcelableString createFromParcel(Parcel in) { + ParcelableString ret = new ParcelableString(); + ret.string = in.readString(); + return ret; + } + @Override + public ParcelableString[] newArray(int size) { + return new ParcelableString[size]; + } + }; + } - /** @hide */ - public static final int REQUEST_OSU_INFO_FAILED = BASE + 5; + private static final int BASE = Protocol.BASE_WIFI_PASSPOINT_MANAGER; - /** @hide */ - public static final int REQUEST_OSU_INFO_SUCCEEDED = BASE + 6; + public static final int REQUEST_ANQP_INFO = BASE + 1; + public static final int REQUEST_ANQP_INFO_FAILED = BASE + 2; + public static final int REQUEST_ANQP_INFO_SUCCEEDED = BASE + 3; + public static final int REQUEST_OSU_ICON = BASE + 4; + public static final int REQUEST_OSU_ICON_FAILED = BASE + 5; + public static final int REQUEST_OSU_ICON_SUCCEEDED = BASE + 6; + public static final int START_OSU = BASE + 7; + public static final int START_OSU_BROWSER = BASE + 8; + public static final int START_OSU_FAILED = BASE + 9; + public static final int START_OSU_SUCCEEDED = BASE + 10; private Context mContext; IWifiPasspointManager mService; @@ -350,7 +389,6 @@ public class WifiPasspointManager { * TODO: doc * @param context * @param service - * @hide */ public WifiPasspointManager(Context context, IWifiPasspointManager service) { mContext = context; @@ -368,7 +406,6 @@ public class WifiPasspointManager { * @return Channel instance that is necessary for performing any further * passpoint operations * - * @hide */ public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) { Messenger messenger = getMessenger(); @@ -387,8 +424,6 @@ public class WifiPasspointManager { /** * STOPSHIP: temp solution, should use supplicant manager instead, check * with b/13931972 - * - * @hide */ public Messenger getMessenger() { try { @@ -398,7 +433,6 @@ public class WifiPasspointManager { } } - /** @hide */ public int getPasspointState() { try { return mService.getPasspointState(); @@ -407,7 +441,6 @@ public class WifiPasspointManager { } } - /** @hide */ public void requestAnqpInfo(Channel c, List<ScanResult> requested, int mask, ActionListener listener) { Log.d(TAG, "requestAnqpInfo start"); @@ -434,14 +467,16 @@ public class WifiPasspointManager { Log.d(TAG, "requestAnqpInfo end"); } - /** @hide */ public void requestOsuIcons(Channel c, List<WifiPasspointOsuProvider> requested, int resolution, ActionListener listener) { } - /** @hide */ public List<WifiPasspointPolicy> requestCredentialMatch(List<ScanResult> requested) { - return null; + try { + return mService.requestCredentialMatch(requested); + } catch (RemoteException e) { + return null; + } } /** @@ -486,21 +521,21 @@ public class WifiPasspointManager { return true; } - /** @hide */ - public void startOsu(Channel c, WifiPasspointOsuProvider selected, OsuRemListener listener) { - + public void startOsu(Channel c, WifiPasspointOsuProvider osu, OsuRemListener listener) { + Log.d(TAG, "startOsu start"); + checkChannel(c); + int key = c.putListener(listener); + c.mAsyncChannel.sendMessage(START_OSU, 0, key, osu); + Log.d(TAG, "startOsu end"); } - /** @hide */ public void startUserRemediation(Channel c, OsuRemListener listener) { } - /** @hide */ - public void connect(WifiPasspointPolicy selected) { + public void connect(WifiPasspointPolicy policy) { } private static void checkChannel(Channel c) { - if (c == null) - throw new IllegalArgumentException("Channel needs to be initialized"); + if (c == null) throw new IllegalArgumentException("Channel needs to be initialized"); } } diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java index f40dc4f..b54b70c 100644 --- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java +++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java @@ -87,12 +87,12 @@ public class WifiPasspointOsuProvider implements Parcelable { @Override public String toString() { StringBuffer sb = new StringBuffer(); - sb.append("SSID: ").append(ssid); + sb.append("SSID: ").append("<").append(ssid).append(">"); if (friendlyName != null) - sb.append(" friendlyName: ").append(friendlyName); + sb.append(" friendlyName: ").append("<").append(friendlyName).append(">"); if (serverUri != null) - sb.append(" serverUri: ").append(serverUri); - sb.append(" osuMethod: ").append(osuMethod); + sb.append(" serverUri: ").append("<").append(serverUri).append(">"); + sb.append(" osuMethod: ").append("<").append(osuMethod).append(">"); if (iconFileName != null) { sb.append(" icon: <").append(iconWidth).append("x") .append(iconHeight).append(" ") @@ -100,9 +100,9 @@ public class WifiPasspointOsuProvider implements Parcelable { .append(iconFileName).append(">"); } if (osuNai != null) - sb.append(" osuNai: ").append(osuNai); + sb.append(" osuNai: ").append("<").append(osuNai).append(">"); if (osuService != null) - sb.append(" osuService: ").append(osuService); + sb.append(" osuService: ").append("<").append(osuService).append(">"); return sb.toString(); } |
