diff options
328 files changed, 12759 insertions, 5545 deletions
@@ -68,6 +68,7 @@ LOCAL_SRC_FILES += \ core/java/android/app/IActivityController.aidl \ core/java/android/app/IActivityPendingResult.aidl \ core/java/android/app/IAlarmManager.aidl \ + core/java/android/app/IAppTask.aidl \ core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ core/java/android/app/INotificationManager.aidl \ @@ -126,6 +127,8 @@ LOCAL_SRC_FILES += \ core/java/android/content/pm/IPackageDeleteObserver.aidl \ core/java/android/content/pm/IPackageInstallObserver.aidl \ core/java/android/content/pm/IPackageInstallObserver2.aidl \ + core/java/android/content/pm/IPackageInstaller.aidl \ + core/java/android/content/pm/IPackageInstallerSession.aidl \ core/java/android/content/pm/IPackageManager.aidl \ core/java/android/content/pm/IPackageMoveObserver.aidl \ core/java/android/content/pm/IPackageStatsObserver.aidl \ diff --git a/api/current.txt b/api/current.txt index 033e395..67f33f4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -29,7 +29,7 @@ package android { field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_ROUTE_PROVIDER = "android.permission.BIND_ROUTE_PROVIDER"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; - field public static final java.lang.String BIND_TRUST_AGENT_SERVICE = "android.permission.BIND_TRUST_AGENT_SERVICE"; + field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT"; field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT"; field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION"; field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE"; @@ -250,7 +250,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 = 16843829; // 0x1010435 + field public static final int actionBarTheme = 16843828; // 0x1010434 field public static final int actionBarWidgetTheme = 16843671; // 0x1010397 field public static final int actionButtonStyle = 16843480; // 0x10102d8 field public static final int actionDropDownStyle = 16843479; // 0x10102d7 @@ -267,7 +267,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 = 16843849; // 0x1010449 + field public static final int actionOverflowMenuStyle = 16843848; // 0x1010448 field public static final int actionProviderClass = 16843657; // 0x1010389 field public static final int actionViewClass = 16843516; // 0x10102fc field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd @@ -313,7 +313,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 = 16843851; // 0x101044b + field public static final int autoRemoveFromRecents = 16843850; // 0x101044a 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 @@ -382,27 +382,27 @@ package android { field public static final int clipChildren = 16842986; // 0x10100ea field public static final int clipOrientation = 16843274; // 0x101020a field public static final int clipToPadding = 16842987; // 0x10100eb - field public static final int clipToPath = 16843818; // 0x101042a + field public static final int clipToPath = 16843817; // 0x1010429 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 = 16843834; // 0x101043a + field public static final int colorAccent = 16843833; // 0x1010439 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 = 16843823; // 0x101042f - field public static final int colorButtonPressed = 16843824; // 0x1010430 - field public static final int colorControlActivated = 16843822; // 0x101042e - field public static final int colorControlNormal = 16843821; // 0x101042d + field public static final int colorButtonNormal = 16843822; // 0x101042e + field public static final int colorButtonPressed = 16843823; // 0x101042f + field public static final int colorControlActivated = 16843821; // 0x101042d + field public static final int colorControlNormal = 16843820; // 0x101042c 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 = 16843832; // 0x1010438 - field public static final int colorPrimaryDark = 16843833; // 0x1010439 - field public static final int colorPrimaryLight = 16843831; // 0x1010437 + field public static final int colorPrimary = 16843831; // 0x1010437 + field public static final int colorPrimaryDark = 16843832; // 0x1010438 + field public static final int colorPrimaryLight = 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 @@ -417,10 +417,14 @@ 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 controlX1 = 16843799; // 0x1010417 - field public static final int controlX2 = 16843801; // 0x1010419 - field public static final int controlY1 = 16843800; // 0x1010418 - field public static final int controlY2 = 16843802; // 0x101041a + 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 controlX1 = 16843798; // 0x1010416 + field public static final int controlX2 = 16843800; // 0x1010418 + field public static final int controlY1 = 16843799; // 0x1010417 + field public static final int controlY2 = 16843801; // 0x1010419 field public static final int cropToPadding = 16843043; // 0x1010123 field public static final int cursorVisible = 16843090; // 0x1010152 field public static final int customNavigationLayout = 16843474; // 0x10102d2 @@ -461,7 +465,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 = 16843850; // 0x101044a + field public static final int documentLaunchMode = 16843849; // 0x1010449 field public static final int drawSelectorOnTop = 16843004; // 0x10100fc field public static final int drawable = 16843161; // 0x1010199 field public static final int drawableBottom = 16843118; // 0x101016e @@ -490,7 +494,7 @@ 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 elevation = 16843845; // 0x1010445 + field public static final int elevation = 16843844; // 0x1010444 field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 field public static final int enabled = 16842766; // 0x101000e @@ -500,10 +504,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 = 16843847; // 0x1010447 + field public static final int excludeClass = 16843846; // 0x1010446 field public static final int excludeFromRecents = 16842775; // 0x1010017 - field public static final int excludeId = 16843846; // 0x1010446 - field public static final int excludeViewName = 16843858; // 0x1010452 + field public static final int excludeId = 16843845; // 0x1010445 + field public static final int excludeViewName = 16843857; // 0x1010451 field public static final int exitFadeDuration = 16843533; // 0x101030d field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052 field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053 @@ -528,15 +532,15 @@ package android { field public static final int fastScrollOverlayPosition = 16843578; // 0x101033a field public static final int fastScrollPreviewBackgroundLeft = 16843575; // 0x1010337 field public static final int fastScrollPreviewBackgroundRight = 16843576; // 0x1010338 - field public static final int fastScrollStyle = 16843794; // 0x1010412 + field public static final int fastScrollStyle = 16843793; // 0x1010411 field public static final int fastScrollTextColor = 16843609; // 0x1010359 field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336 field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339 - field public static final int fill = 16843808; // 0x1010420 + field public static final int fill = 16843807; // 0x101041f field public static final int fillAfter = 16843197; // 0x10101bd field public static final int fillBefore = 16843196; // 0x10101bc field public static final int fillEnabled = 16843343; // 0x101024f - field public static final int fillOpacity = 16843807; // 0x101041f + field public static final int fillOpacity = 16843806; // 0x101041e field public static final int fillViewport = 16843130; // 0x101017a field public static final int filter = 16843035; // 0x101011b field public static final int filterTouchesWhenObscured = 16843460; // 0x10102c4 @@ -556,7 +560,6 @@ package android { field public static final int format12Hour = 16843722; // 0x10103ca field public static final int format24Hour = 16843723; // 0x10103cb field public static final int fragment = 16843491; // 0x10102e3 - field public static final int fragmentBreadCrumbsStyle = 16843793; // 0x1010411 field public static final int fragmentCloseEnterAnimation = 16843495; // 0x10102e7 field public static final int fragmentCloseExitAnimation = 16843496; // 0x10102e8 field public static final int fragmentFadeEnterAnimation = 16843497; // 0x10102e9 @@ -566,7 +569,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 = 16843854; // 0x101044e + field public static final int fromId = 16843853; // 0x101044d field public static final int fromScene = 16843741; // 0x10103dd field public static final int fromXDelta = 16843206; // 0x10101c6 field public static final int fromXScale = 16843202; // 0x10101c2 @@ -599,7 +602,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 = 16843848; // 0x1010448 + field public static final int hideOnContentScroll = 16843847; // 0x1010447 field public static final int hint = 16843088; // 0x1010150 field public static final int homeAsUpIndicator = 16843531; // 0x101030b field public static final int homeLayout = 16843549; // 0x101031d @@ -796,6 +799,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 max = 16843062; // 0x1010136 field public static final int maxDate = 16843584; // 0x1010340 field public static final int maxEms = 16843095; // 0x1010157 @@ -828,9 +832,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 navigationMode = 16843471; // 0x10102cf field public static final int negativeButtonText = 16843254; // 0x10101f6 - field public static final int nestedScrollingEnabled = 16843835; // 0x101043b + field public static final int nestedScrollingEnabled = 16843834; // 0x101043a field public static final int nextFocusDown = 16842980; // 0x10100e4 field public static final int nextFocusForward = 16843580; // 0x101033c field public static final int nextFocusLeft = 16842977; // 0x10100e1 @@ -872,18 +877,18 @@ package android { field public static final int parentActivityName = 16843687; // 0x10103a7 field public static final deprecated int password = 16843100; // 0x101015c field public static final int path = 16842794; // 0x101002a - field public static final int pathData = 16843809; // 0x1010421 + field public static final int pathData = 16843808; // 0x1010420 field public static final int pathPattern = 16842796; // 0x101002c field public static final int pathPrefix = 16842795; // 0x101002b field public static final int permission = 16842758; // 0x1010006 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 = 16843825; // 0x1010431 + field public static final int persistable = 16843824; // 0x1010430 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 = 16843820; // 0x101042c + 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 @@ -947,7 +952,7 @@ package android { field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 field public static final int requiredForAllUsers = 16843728; // 0x10103d0 - field public static final int requiredForProfile = 16843819; // 0x101042b + field public static final int requiredForProfile = 16843818; // 0x101042a field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 field public static final int resizeMode = 16843619; // 0x1010363 @@ -956,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 = 16843855; // 0x101044f + field public static final int reversible = 16843854; // 0x101044e field public static final int right = 16843183; // 0x10101af field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093 field public static final int ringtoneType = 16843257; // 0x10101f9 @@ -1012,7 +1017,7 @@ package android { field public static final int selectableItemBackground = 16843534; // 0x101030e field public static final int selectedDateVerticalBar = 16843591; // 0x1010347 field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342 - field public static final int sessionService = 16843842; // 0x1010442 + field public static final int sessionService = 16843841; // 0x1010441 field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int shadowColor = 16843105; // 0x1010161 field public static final int shadowDx = 16843106; // 0x1010162 @@ -1033,7 +1038,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 = 16843828; // 0x1010434 + field public static final int slideEdge = 16843827; // 0x1010433 field public static final int smallIcon = 16843422; // 0x101029e field public static final int smallScreens = 16843396; // 0x1010284 field public static final int smoothScrollbar = 16843313; // 0x1010231 @@ -1045,19 +1050,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 = 16843856; // 0x1010450 + field public static final int splitTrack = 16843855; // 0x101044f 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 = 16843843; // 0x1010443 + field public static final int stackViewStyle = 16843842; // 0x1010442 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 = 16843852; // 0x101044c + field public static final int stateListAnimator = 16843851; // 0x101044b 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 @@ -1082,18 +1087,19 @@ 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 stepSize = 16843078; // 0x1010146 field public static final int stopWithTask = 16843626; // 0x101036a field public static final int streamType = 16843273; // 0x1010209 field public static final int stretchColumns = 16843081; // 0x1010149 field public static final int stretchMode = 16843030; // 0x1010116 - field public static final int stroke = 16843810; // 0x1010422 - field public static final int strokeLineCap = 16843816; // 0x1010428 - field public static final int strokeLineJoin = 16843817; // 0x1010429 - field public static final int strokeOpacity = 16843811; // 0x1010423 - field public static final int strokeWidth = 16843812; // 0x1010424 + field public static final int stroke = 16843809; // 0x1010421 + field public static final int strokeLineCap = 16843815; // 0x1010427 + field public static final int strokeLineJoin = 16843816; // 0x1010428 + 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 = 16843827; // 0x1010433 + field public static final int subtitleTextAppearance = 16843826; // 0x1010432 field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int subtypeExtraValue = 16843674; // 0x101039a field public static final int subtypeId = 16843713; // 0x10103c1 @@ -1110,7 +1116,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 = 16843844; // 0x1010444 + field public static final int switchStyle = 16843843; // 0x1010443 field public static final int switchTextAppearance = 16843630; // 0x101036e field public static final int switchTextOff = 16843628; // 0x101036c field public static final int switchTextOn = 16843627; // 0x101036b @@ -1126,7 +1132,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 = 16843857; // 0x1010451 + field public static final int targetViewName = 16843856; // 0x1010450 field public static final int taskAffinity = 16842770; // 0x1010012 field public static final int taskCloseEnterAnimation = 16842942; // 0x10100be field public static final int taskCloseExitAnimation = 16842943; // 0x10100bf @@ -1148,7 +1154,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 = 16843830; // 0x1010436 + field public static final int textAppearanceListItemSecondary = 16843829; // 0x1010435 field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f field public static final int textAppearanceMedium = 16842817; // 0x1010041 field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044 @@ -1209,14 +1215,14 @@ package android { field public static final int tileMode = 16843265; // 0x1010201 field public static final int timeZone = 16843724; // 0x10103cc field public static final int tint = 16843041; // 0x1010121 - field public static final int tintMode = 16843798; // 0x1010416 + 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 = 16843826; // 0x1010432 + field public static final int titleTextAppearance = 16843825; // 0x1010431 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 = 16843853; // 0x101044d + field public static final int toId = 16843852; // 0x101044c field public static final int toScene = 16843742; // 0x10103de field public static final int toXDelta = 16843207; // 0x10101c7 field public static final int toXScale = 16843203; // 0x10101c3 @@ -1233,14 +1239,14 @@ package android { field public static final int transformPivotX = 16843552; // 0x1010320 field public static final int transformPivotY = 16843553; // 0x1010321 field public static final int transition = 16843743; // 0x10103df - field public static final int transitionGroup = 16843804; // 0x101041c + field public static final int transitionGroup = 16843803; // 0x101041b field public static final int transitionOrdering = 16843744; // 0x10103e0 field public static final int translationX = 16843554; // 0x1010322 field public static final int translationY = 16843555; // 0x1010323 - field public static final int translationZ = 16843797; // 0x1010415 - field public static final int trimPathEnd = 16843814; // 0x1010426 - field public static final int trimPathOffset = 16843815; // 0x1010427 - field public static final int trimPathStart = 16843813; // 0x1010425 + field public static final int translationZ = 16843796; // 0x1010414 + field public static final int trimPathEnd = 16843813; // 0x1010425 + field public static final int trimPathOffset = 16843814; // 0x1010426 + field public static final int trimPathStart = 16843812; // 0x1010424 field public static final int type = 16843169; // 0x10101a1 field public static final int typeface = 16842902; // 0x1010096 field public static final int uiOptions = 16843672; // 0x1010398 @@ -1265,9 +1271,9 @@ package android { field public static final int verticalGap = 16843328; // 0x1010240 field public static final int verticalScrollbarPosition = 16843572; // 0x1010334 field public static final int verticalSpacing = 16843029; // 0x1010115 - field public static final int viewName = 16843803; // 0x101041b - field public static final int viewportHeight = 16843806; // 0x101041e - field public static final int viewportWidth = 16843805; // 0x101041d + field public static final int viewName = 16843802; // 0x101041a + field public static final int viewportHeight = 16843805; // 0x101041d + field public static final int viewportWidth = 16843804; // 0x101041c field public static final int visibility = 16842972; // 0x10100dc field public static final int visible = 16843156; // 0x1010194 field public static final int vmSafeMode = 16843448; // 0x10102b8 @@ -1296,20 +1302,21 @@ 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 = 16843841; // 0x1010441 - field public static final int windowAllowExitTransitionOverlap = 16843840; // 0x1010440 + field public static final int windowAllowEnterTransitionOverlap = 16843840; // 0x1010440 + field public static final int windowAllowExitTransitionOverlap = 16843839; // 0x101043f field public static final int windowAnimationStyle = 16842926; // 0x10100ae field public static final int windowBackground = 16842836; // 0x1010054 field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b field public static final int windowContentOverlay = 16842841; // 0x1010059 - field public static final int windowContentTransitionManager = 16843796; // 0x1010414 - field public static final int windowContentTransitions = 16843795; // 0x1010413 + 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 windowEnableSplitTouch = 16843543; // 0x1010317 field public static final int windowEnterAnimation = 16842932; // 0x10100b4 - field public static final int windowEnterTransition = 16843836; // 0x101043c + field public static final int windowEnterTransition = 16843835; // 0x101043b field public static final int windowExitAnimation = 16842933; // 0x10100b5 - field public static final int windowExitTransition = 16843837; // 0x101043d + field public static final int windowExitTransition = 16843836; // 0x101043c field public static final int windowFrame = 16842837; // 0x1010055 field public static final int windowFullscreen = 16843277; // 0x101020d field public static final int windowHideAnimation = 16842935; // 0x10100b7 @@ -1320,8 +1327,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 = 16843838; // 0x101043e - field public static final int windowSharedElementExitTransition = 16843839; // 0x101043f + field public static final int windowSharedElementEnterTransition = 16843837; // 0x101043d + field public static final int windowSharedElementExitTransition = 16843838; // 0x101043e field public static final int windowShowAnimation = 16842934; // 0x10100b6 field public static final int windowShowWallpaper = 16843410; // 0x1010292 field public static final int windowSoftInputMode = 16843307; // 0x101022b @@ -1858,52 +1865,52 @@ package android { field public static final int TextAppearance_Large_Inverse = 16973891; // 0x1030043 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_Quantum = 16974352; // 0x1030210 - field public static final int TextAppearance_Quantum_Body1 = 16974544; // 0x10302d0 - field public static final int TextAppearance_Quantum_Body2 = 16974543; // 0x10302cf - field public static final int TextAppearance_Quantum_Button = 16974547; // 0x10302d3 - field public static final int TextAppearance_Quantum_Caption = 16974545; // 0x10302d1 - field public static final int TextAppearance_Quantum_DialogWindowTitle = 16974353; // 0x1030211 - field public static final int TextAppearance_Quantum_Display1 = 16974539; // 0x10302cb - field public static final int TextAppearance_Quantum_Display2 = 16974538; // 0x10302ca - field public static final int TextAppearance_Quantum_Display3 = 16974537; // 0x10302c9 - field public static final int TextAppearance_Quantum_Display4 = 16974536; // 0x10302c8 - field public static final int TextAppearance_Quantum_Headline = 16974540; // 0x10302cc - field public static final int TextAppearance_Quantum_Inverse = 16974354; // 0x1030212 - field public static final int TextAppearance_Quantum_Large = 16974355; // 0x1030213 - field public static final int TextAppearance_Quantum_Large_Inverse = 16974356; // 0x1030214 - field public static final int TextAppearance_Quantum_Medium = 16974357; // 0x1030215 - field public static final int TextAppearance_Quantum_Medium_Inverse = 16974358; // 0x1030216 - field public static final int TextAppearance_Quantum_Menu = 16974546; // 0x10302d2 - field public static final int TextAppearance_Quantum_SearchResult_Subtitle = 16974359; // 0x1030217 - field public static final int TextAppearance_Quantum_SearchResult_Title = 16974360; // 0x1030218 - field public static final int TextAppearance_Quantum_Small = 16974361; // 0x1030219 - field public static final int TextAppearance_Quantum_Small_Inverse = 16974362; // 0x103021a - field public static final int TextAppearance_Quantum_Subhead = 16974542; // 0x10302ce - field public static final int TextAppearance_Quantum_Title = 16974541; // 0x10302cd - field public static final int TextAppearance_Quantum_Widget = 16974364; // 0x103021c - field public static final int TextAppearance_Quantum_Widget_ActionBar_Menu = 16974365; // 0x103021d - field public static final int TextAppearance_Quantum_Widget_ActionBar_Subtitle = 16974366; // 0x103021e - field public static final int TextAppearance_Quantum_Widget_ActionBar_Subtitle_Inverse = 16974367; // 0x103021f - field public static final int TextAppearance_Quantum_Widget_ActionBar_Title = 16974368; // 0x1030220 - field public static final int TextAppearance_Quantum_Widget_ActionBar_Title_Inverse = 16974369; // 0x1030221 - field public static final int TextAppearance_Quantum_Widget_ActionMode_Subtitle = 16974370; // 0x1030222 - field public static final int TextAppearance_Quantum_Widget_ActionMode_Subtitle_Inverse = 16974371; // 0x1030223 - field public static final int TextAppearance_Quantum_Widget_ActionMode_Title = 16974372; // 0x1030224 - field public static final int TextAppearance_Quantum_Widget_ActionMode_Title_Inverse = 16974373; // 0x1030225 - field public static final int TextAppearance_Quantum_Widget_Button = 16974374; // 0x1030226 - field public static final int TextAppearance_Quantum_Widget_DropDownHint = 16974375; // 0x1030227 - field public static final int TextAppearance_Quantum_Widget_DropDownItem = 16974376; // 0x1030228 - field public static final int TextAppearance_Quantum_Widget_EditText = 16974377; // 0x1030229 - field public static final int TextAppearance_Quantum_Widget_IconMenu_Item = 16974378; // 0x103022a - field public static final int TextAppearance_Quantum_Widget_PopupMenu = 16974379; // 0x103022b - field public static final int TextAppearance_Quantum_Widget_PopupMenu_Large = 16974380; // 0x103022c - field public static final int TextAppearance_Quantum_Widget_PopupMenu_Small = 16974381; // 0x103022d - field public static final int TextAppearance_Quantum_Widget_TabWidget = 16974382; // 0x103022e - field public static final int TextAppearance_Quantum_Widget_TextView = 16974383; // 0x103022f - field public static final int TextAppearance_Quantum_Widget_TextView_PopupMenu = 16974384; // 0x1030230 - field public static final int TextAppearance_Quantum_Widget_TextView_SpinnerItem = 16974385; // 0x1030231 - field public static final int TextAppearance_Quantum_WindowTitle = 16974363; // 0x103021b + field public static final int TextAppearance_Quantum = 16974348; // 0x103020c + field public static final int TextAppearance_Quantum_Body1 = 16974543; // 0x10302cf + field public static final int TextAppearance_Quantum_Body2 = 16974542; // 0x10302ce + field public static final int TextAppearance_Quantum_Button = 16974546; // 0x10302d2 + field public static final int TextAppearance_Quantum_Caption = 16974544; // 0x10302d0 + field public static final int TextAppearance_Quantum_DialogWindowTitle = 16974349; // 0x103020d + field public static final int TextAppearance_Quantum_Display1 = 16974538; // 0x10302ca + field public static final int TextAppearance_Quantum_Display2 = 16974537; // 0x10302c9 + field public static final int TextAppearance_Quantum_Display3 = 16974536; // 0x10302c8 + field public static final int TextAppearance_Quantum_Display4 = 16974535; // 0x10302c7 + field public static final int TextAppearance_Quantum_Headline = 16974539; // 0x10302cb + field public static final int TextAppearance_Quantum_Inverse = 16974350; // 0x103020e + field public static final int TextAppearance_Quantum_Large = 16974351; // 0x103020f + field public static final int TextAppearance_Quantum_Large_Inverse = 16974352; // 0x1030210 + field public static final int TextAppearance_Quantum_Medium = 16974353; // 0x1030211 + field public static final int TextAppearance_Quantum_Medium_Inverse = 16974354; // 0x1030212 + field public static final int TextAppearance_Quantum_Menu = 16974545; // 0x10302d1 + field public static final int TextAppearance_Quantum_SearchResult_Subtitle = 16974355; // 0x1030213 + field public static final int TextAppearance_Quantum_SearchResult_Title = 16974356; // 0x1030214 + field public static final int TextAppearance_Quantum_Small = 16974357; // 0x1030215 + field public static final int TextAppearance_Quantum_Small_Inverse = 16974358; // 0x1030216 + field public static final int TextAppearance_Quantum_Subhead = 16974541; // 0x10302cd + field public static final int TextAppearance_Quantum_Title = 16974540; // 0x10302cc + field public static final int TextAppearance_Quantum_Widget = 16974360; // 0x1030218 + field public static final int TextAppearance_Quantum_Widget_ActionBar_Menu = 16974361; // 0x1030219 + field public static final int TextAppearance_Quantum_Widget_ActionBar_Subtitle = 16974362; // 0x103021a + field public static final int TextAppearance_Quantum_Widget_ActionBar_Subtitle_Inverse = 16974363; // 0x103021b + field public static final int TextAppearance_Quantum_Widget_ActionBar_Title = 16974364; // 0x103021c + field public static final int TextAppearance_Quantum_Widget_ActionBar_Title_Inverse = 16974365; // 0x103021d + field public static final int TextAppearance_Quantum_Widget_ActionMode_Subtitle = 16974366; // 0x103021e + field public static final int TextAppearance_Quantum_Widget_ActionMode_Subtitle_Inverse = 16974367; // 0x103021f + field public static final int TextAppearance_Quantum_Widget_ActionMode_Title = 16974368; // 0x1030220 + field public static final int TextAppearance_Quantum_Widget_ActionMode_Title_Inverse = 16974369; // 0x1030221 + field public static final int TextAppearance_Quantum_Widget_Button = 16974370; // 0x1030222 + field public static final int TextAppearance_Quantum_Widget_DropDownHint = 16974371; // 0x1030223 + field public static final int TextAppearance_Quantum_Widget_DropDownItem = 16974372; // 0x1030224 + field public static final int TextAppearance_Quantum_Widget_EditText = 16974373; // 0x1030225 + field public static final int TextAppearance_Quantum_Widget_IconMenu_Item = 16974374; // 0x1030226 + field public static final int TextAppearance_Quantum_Widget_PopupMenu = 16974375; // 0x1030227 + field public static final int TextAppearance_Quantum_Widget_PopupMenu_Large = 16974376; // 0x1030228 + field public static final int TextAppearance_Quantum_Widget_PopupMenu_Small = 16974377; // 0x1030229 + field public static final int TextAppearance_Quantum_Widget_TabWidget = 16974378; // 0x103022a + field public static final int TextAppearance_Quantum_Widget_TextView = 16974379; // 0x103022b + field public static final int TextAppearance_Quantum_Widget_TextView_PopupMenu = 16974380; // 0x103022c + field public static final int TextAppearance_Quantum_Widget_TextView_SpinnerItem = 16974381; // 0x103022d + field public static final int TextAppearance_Quantum_WindowTitle = 16974359; // 0x1030217 field public static final int TextAppearance_Small = 16973894; // 0x1030046 field public static final int TextAppearance_Small_Inverse = 16973895; // 0x1030047 field public static final int TextAppearance_StatusBar_EventContent = 16973927; // 0x1030067 @@ -1927,6 +1934,11 @@ 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 = 16974410; // 0x103024a + field public static final int ThemeOverlay_Quantum = 16974411; // 0x103024b + field public static final int ThemeOverlay_Quantum_ActionBarWidget = 16974414; // 0x103024e + field public static final int ThemeOverlay_Quantum_Dark = 16974413; // 0x103024d + field public static final int ThemeOverlay_Quantum_Light = 16974412; // 0x103024c 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 @@ -1998,34 +2010,34 @@ package android { field public static final int Theme_NoTitleBar_Fullscreen = 16973831; // 0x1030007 field public static final int Theme_NoTitleBar_OverlayActionModes = 16973930; // 0x103006a field public static final int Theme_Panel = 16973913; // 0x1030059 - field public static final int Theme_Quantum = 16974386; // 0x1030232 - field public static final int Theme_Quantum_Dialog = 16974387; // 0x1030233 - field public static final int Theme_Quantum_DialogWhenLarge = 16974391; // 0x1030237 - field public static final int Theme_Quantum_DialogWhenLarge_NoActionBar = 16974392; // 0x1030238 - field public static final int Theme_Quantum_Dialog_MinWidth = 16974388; // 0x1030234 - field public static final int Theme_Quantum_Dialog_NoActionBar = 16974389; // 0x1030235 - field public static final int Theme_Quantum_Dialog_NoActionBar_MinWidth = 16974390; // 0x1030236 - field public static final int Theme_Quantum_InputMethod = 16974393; // 0x1030239 - field public static final int Theme_Quantum_Light = 16974401; // 0x1030241 - field public static final int Theme_Quantum_Light_DarkActionBar = 16974402; // 0x1030242 - field public static final int Theme_Quantum_Light_Dialog = 16974403; // 0x1030243 - field public static final int Theme_Quantum_Light_DialogWhenLarge = 16974407; // 0x1030247 - field public static final int Theme_Quantum_Light_DialogWhenLarge_NoActionBar = 16974408; // 0x1030248 - field public static final int Theme_Quantum_Light_Dialog_MinWidth = 16974404; // 0x1030244 - field public static final int Theme_Quantum_Light_Dialog_NoActionBar = 16974405; // 0x1030245 - field public static final int Theme_Quantum_Light_Dialog_NoActionBar_MinWidth = 16974406; // 0x1030246 - field public static final int Theme_Quantum_Light_NoActionBar = 16974409; // 0x1030249 - field public static final int Theme_Quantum_Light_NoActionBar_Fullscreen = 16974410; // 0x103024a - field public static final int Theme_Quantum_Light_NoActionBar_Overscan = 16974411; // 0x103024b - field public static final int Theme_Quantum_Light_NoActionBar_TranslucentDecor = 16974412; // 0x103024c - field public static final int Theme_Quantum_Light_Panel = 16974413; // 0x103024d - field public static final int Theme_Quantum_NoActionBar = 16974394; // 0x103023a - field public static final int Theme_Quantum_NoActionBar_Fullscreen = 16974395; // 0x103023b - field public static final int Theme_Quantum_NoActionBar_Overscan = 16974396; // 0x103023c - field public static final int Theme_Quantum_NoActionBar_TranslucentDecor = 16974397; // 0x103023d - field public static final int Theme_Quantum_Panel = 16974398; // 0x103023e - field public static final int Theme_Quantum_Wallpaper = 16974399; // 0x103023f - field public static final int Theme_Quantum_Wallpaper_NoTitleBar = 16974400; // 0x1030240 + field public static final int Theme_Quantum = 16974382; // 0x103022e + field public static final int Theme_Quantum_Dialog = 16974383; // 0x103022f + field public static final int Theme_Quantum_DialogWhenLarge = 16974387; // 0x1030233 + field public static final int Theme_Quantum_DialogWhenLarge_NoActionBar = 16974388; // 0x1030234 + field public static final int Theme_Quantum_Dialog_MinWidth = 16974384; // 0x1030230 + field public static final int Theme_Quantum_Dialog_NoActionBar = 16974385; // 0x1030231 + field public static final int Theme_Quantum_Dialog_NoActionBar_MinWidth = 16974386; // 0x1030232 + field public static final int Theme_Quantum_InputMethod = 16974389; // 0x1030235 + field public static final int Theme_Quantum_Light = 16974397; // 0x103023d + field public static final int Theme_Quantum_Light_DarkActionBar = 16974398; // 0x103023e + field public static final int Theme_Quantum_Light_Dialog = 16974399; // 0x103023f + field public static final int Theme_Quantum_Light_DialogWhenLarge = 16974403; // 0x1030243 + field public static final int Theme_Quantum_Light_DialogWhenLarge_NoActionBar = 16974404; // 0x1030244 + field public static final int Theme_Quantum_Light_Dialog_MinWidth = 16974400; // 0x1030240 + field public static final int Theme_Quantum_Light_Dialog_NoActionBar = 16974401; // 0x1030241 + field public static final int Theme_Quantum_Light_Dialog_NoActionBar_MinWidth = 16974402; // 0x1030242 + field public static final int Theme_Quantum_Light_NoActionBar = 16974405; // 0x1030245 + field public static final int Theme_Quantum_Light_NoActionBar_Fullscreen = 16974406; // 0x1030246 + field public static final int Theme_Quantum_Light_NoActionBar_Overscan = 16974407; // 0x1030247 + field public static final int Theme_Quantum_Light_NoActionBar_TranslucentDecor = 16974408; // 0x1030248 + field public static final int Theme_Quantum_Light_Panel = 16974409; // 0x1030249 + field public static final int Theme_Quantum_NoActionBar = 16974390; // 0x1030236 + field public static final int Theme_Quantum_NoActionBar_Fullscreen = 16974391; // 0x1030237 + field public static final int Theme_Quantum_NoActionBar_Overscan = 16974392; // 0x1030238 + field public static final int Theme_Quantum_NoActionBar_TranslucentDecor = 16974393; // 0x1030239 + field public static final int Theme_Quantum_Panel = 16974394; // 0x103023a + field public static final int Theme_Quantum_Wallpaper = 16974395; // 0x103023b + field public static final int Theme_Quantum_Wallpaper_NoTitleBar = 16974396; // 0x103023c field public static final int Theme_Translucent = 16973839; // 0x103000f field public static final int Theme_Translucent_NoTitleBar = 16973840; // 0x1030010 field public static final int Theme_Translucent_NoTitleBar_Fullscreen = 16973841; // 0x1030011 @@ -2082,8 +2094,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 = 16974346; // 0x103020a - field public static final int Widget_DeviceDefault_FragmentBreadCrumbs = 16974347; // 0x103020b + field public static final int Widget_DeviceDefault_FastScroll = 16974344; // 0x1030208 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 @@ -2117,8 +2128,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 = 16974349; // 0x103020d - field public static final int Widget_DeviceDefault_Light_FragmentBreadCrumbs = 16974350; // 0x103020e + field public static final int Widget_DeviceDefault_Light_FastScroll = 16974346; // 0x103020a 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 @@ -2142,7 +2152,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 = 16974351; // 0x103020f + field public static final int Widget_DeviceDefault_Light_StackView = 16974347; // 0x103020b 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 @@ -2166,7 +2176,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 = 16974348; // 0x103020c + field public static final int Widget_DeviceDefault_StackView = 16974345; // 0x1030209 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 @@ -2210,7 +2220,6 @@ package android { 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_FragmentBreadCrumbs = 16974340; // 0x1030204 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 @@ -2231,7 +2240,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 = 16974342; // 0x1030206 + field public static final int Widget_Holo_Light_Button_Borderless = 16974341; // 0x1030205 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 @@ -2245,8 +2254,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 = 16974343; // 0x1030207 - field public static final int Widget_Holo_Light_FragmentBreadCrumbs = 16974344; // 0x1030208 + field public static final int Widget_Holo_Light_FastScroll = 16974342; // 0x1030206 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 @@ -2270,7 +2278,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 = 16974345; // 0x1030209 + field public static final int Widget_Holo_Light_StackView = 16974343; // 0x1030207 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 @@ -2294,7 +2302,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 = 16974341; // 0x1030205 + field public static final int Widget_Holo_StackView = 16974340; // 0x1030204 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 @@ -2318,37 +2326,36 @@ package android { field public static final int Widget_ProgressBar_Large_Inverse = 16973916; // 0x103005c field public static final int Widget_ProgressBar_Small = 16973854; // 0x103001e field public static final int Widget_ProgressBar_Small_Inverse = 16973917; // 0x103005d - field public static final int Widget_Quantum = 16974414; // 0x103024e - field public static final int Widget_Quantum_ActionBar = 16974415; // 0x103024f - field public static final int Widget_Quantum_ActionBar_Solid = 16974416; // 0x1030250 - field public static final int Widget_Quantum_ActionBar_TabBar = 16974417; // 0x1030251 - field public static final int Widget_Quantum_ActionBar_TabText = 16974418; // 0x1030252 - field public static final int Widget_Quantum_ActionBar_TabView = 16974419; // 0x1030253 - field public static final int Widget_Quantum_ActionButton = 16974420; // 0x1030254 - field public static final int Widget_Quantum_ActionButton_CloseMode = 16974421; // 0x1030255 - field public static final int Widget_Quantum_ActionButton_Overflow = 16974422; // 0x1030256 - field public static final int Widget_Quantum_ActionMode = 16974423; // 0x1030257 - field public static final int Widget_Quantum_AutoCompleteTextView = 16974424; // 0x1030258 - field public static final int Widget_Quantum_Button = 16974425; // 0x1030259 - field public static final int Widget_Quantum_ButtonBar = 16974431; // 0x103025f - field public static final int Widget_Quantum_ButtonBar_AlertDialog = 16974432; // 0x1030260 - field public static final int Widget_Quantum_Button_Borderless = 16974426; // 0x103025a - field public static final int Widget_Quantum_Button_Borderless_Small = 16974427; // 0x103025b - field public static final int Widget_Quantum_Button_Inset = 16974428; // 0x103025c - field public static final int Widget_Quantum_Button_Small = 16974429; // 0x103025d - field public static final int Widget_Quantum_Button_Toggle = 16974430; // 0x103025e - field public static final int Widget_Quantum_CalendarView = 16974433; // 0x1030261 - field public static final int Widget_Quantum_CheckedTextView = 16974434; // 0x1030262 - field public static final int Widget_Quantum_CompoundButton_CheckBox = 16974435; // 0x1030263 - field public static final int Widget_Quantum_CompoundButton_RadioButton = 16974436; // 0x1030264 - field public static final int Widget_Quantum_CompoundButton_Star = 16974437; // 0x1030265 - field public static final int Widget_Quantum_DatePicker = 16974438; // 0x1030266 - field public static final int Widget_Quantum_DropDownItem = 16974439; // 0x1030267 - field public static final int Widget_Quantum_DropDownItem_Spinner = 16974440; // 0x1030268 - field public static final int Widget_Quantum_EditText = 16974441; // 0x1030269 - field public static final int Widget_Quantum_ExpandableListView = 16974442; // 0x103026a - field public static final int Widget_Quantum_FastScroll = 16974443; // 0x103026b - field public static final int Widget_Quantum_FragmentBreadCrumbs = 16974444; // 0x103026c + field public static final int Widget_Quantum = 16974415; // 0x103024f + field public static final int Widget_Quantum_ActionBar = 16974416; // 0x1030250 + field public static final int Widget_Quantum_ActionBar_Solid = 16974417; // 0x1030251 + field public static final int Widget_Quantum_ActionBar_TabBar = 16974418; // 0x1030252 + field public static final int Widget_Quantum_ActionBar_TabText = 16974419; // 0x1030253 + field public static final int Widget_Quantum_ActionBar_TabView = 16974420; // 0x1030254 + field public static final int Widget_Quantum_ActionButton = 16974421; // 0x1030255 + field public static final int Widget_Quantum_ActionButton_CloseMode = 16974422; // 0x1030256 + field public static final int Widget_Quantum_ActionButton_Overflow = 16974423; // 0x1030257 + field public static final int Widget_Quantum_ActionMode = 16974424; // 0x1030258 + field public static final int Widget_Quantum_AutoCompleteTextView = 16974425; // 0x1030259 + field public static final int Widget_Quantum_Button = 16974426; // 0x103025a + field public static final int Widget_Quantum_ButtonBar = 16974432; // 0x1030260 + field public static final int Widget_Quantum_ButtonBar_AlertDialog = 16974433; // 0x1030261 + field public static final int Widget_Quantum_Button_Borderless = 16974427; // 0x103025b + field public static final int Widget_Quantum_Button_Borderless_Small = 16974428; // 0x103025c + field public static final int Widget_Quantum_Button_Inset = 16974429; // 0x103025d + field public static final int Widget_Quantum_Button_Small = 16974430; // 0x103025e + field public static final int Widget_Quantum_Button_Toggle = 16974431; // 0x103025f + field public static final int Widget_Quantum_CalendarView = 16974434; // 0x1030262 + field public static final int Widget_Quantum_CheckedTextView = 16974435; // 0x1030263 + field public static final int Widget_Quantum_CompoundButton_CheckBox = 16974436; // 0x1030264 + field public static final int Widget_Quantum_CompoundButton_RadioButton = 16974437; // 0x1030265 + field public static final int Widget_Quantum_CompoundButton_Star = 16974438; // 0x1030266 + field public static final int Widget_Quantum_DatePicker = 16974439; // 0x1030267 + field public static final int Widget_Quantum_DropDownItem = 16974440; // 0x1030268 + field public static final int Widget_Quantum_DropDownItem_Spinner = 16974441; // 0x1030269 + field public static final int Widget_Quantum_EditText = 16974442; // 0x103026a + field public static final int Widget_Quantum_ExpandableListView = 16974443; // 0x103026b + field public static final int Widget_Quantum_FastScroll = 16974444; // 0x103026c field public static final int Widget_Quantum_GridView = 16974445; // 0x103026d field public static final int Widget_Quantum_HorizontalScrollView = 16974446; // 0x103026e field public static final int Widget_Quantum_ImageButton = 16974447; // 0x103026f @@ -2381,39 +2388,38 @@ package android { field public static final int Widget_Quantum_Light_EditText = 16974500; // 0x10302a4 field public static final int Widget_Quantum_Light_ExpandableListView = 16974501; // 0x10302a5 field public static final int Widget_Quantum_Light_FastScroll = 16974502; // 0x10302a6 - field public static final int Widget_Quantum_Light_FragmentBreadCrumbs = 16974503; // 0x10302a7 - field public static final int Widget_Quantum_Light_GridView = 16974504; // 0x10302a8 - field public static final int Widget_Quantum_Light_HorizontalScrollView = 16974505; // 0x10302a9 - field public static final int Widget_Quantum_Light_ImageButton = 16974506; // 0x10302aa - field public static final int Widget_Quantum_Light_ListPopupWindow = 16974507; // 0x10302ab - field public static final int Widget_Quantum_Light_ListView = 16974508; // 0x10302ac - field public static final int Widget_Quantum_Light_ListView_DropDown = 16974509; // 0x10302ad - field public static final int Widget_Quantum_Light_MediaRouteButton = 16974510; // 0x10302ae - field public static final int Widget_Quantum_Light_PopupMenu = 16974511; // 0x10302af - field public static final int Widget_Quantum_Light_PopupMenu_Overflow = 16974512; // 0x10302b0 - field public static final int Widget_Quantum_Light_PopupWindow = 16974513; // 0x10302b1 - field public static final int Widget_Quantum_Light_ProgressBar = 16974514; // 0x10302b2 - field public static final int Widget_Quantum_Light_ProgressBar_Horizontal = 16974515; // 0x10302b3 - field public static final int Widget_Quantum_Light_ProgressBar_Inverse = 16974516; // 0x10302b4 - field public static final int Widget_Quantum_Light_ProgressBar_Large = 16974517; // 0x10302b5 - field public static final int Widget_Quantum_Light_ProgressBar_Large_Inverse = 16974518; // 0x10302b6 - field public static final int Widget_Quantum_Light_ProgressBar_Small = 16974519; // 0x10302b7 - field public static final int Widget_Quantum_Light_ProgressBar_Small_Inverse = 16974520; // 0x10302b8 - field public static final int Widget_Quantum_Light_ProgressBar_Small_Title = 16974521; // 0x10302b9 - field public static final int Widget_Quantum_Light_RatingBar = 16974522; // 0x10302ba - field public static final int Widget_Quantum_Light_RatingBar_Indicator = 16974523; // 0x10302bb - field public static final int Widget_Quantum_Light_RatingBar_Small = 16974524; // 0x10302bc - field public static final int Widget_Quantum_Light_ScrollView = 16974525; // 0x10302bd - field public static final int Widget_Quantum_Light_SeekBar = 16974526; // 0x10302be - field public static final int Widget_Quantum_Light_SegmentedButton = 16974527; // 0x10302bf - field public static final int Widget_Quantum_Light_Spinner = 16974529; // 0x10302c1 - field public static final int Widget_Quantum_Light_StackView = 16974528; // 0x10302c0 - field public static final int Widget_Quantum_Light_Tab = 16974530; // 0x10302c2 - field public static final int Widget_Quantum_Light_TabWidget = 16974531; // 0x10302c3 - field public static final int Widget_Quantum_Light_TextView = 16974532; // 0x10302c4 - field public static final int Widget_Quantum_Light_TextView_SpinnerItem = 16974533; // 0x10302c5 - field public static final int Widget_Quantum_Light_WebTextView = 16974534; // 0x10302c6 - field public static final int Widget_Quantum_Light_WebView = 16974535; // 0x10302c7 + field public static final int Widget_Quantum_Light_GridView = 16974503; // 0x10302a7 + field public static final int Widget_Quantum_Light_HorizontalScrollView = 16974504; // 0x10302a8 + field public static final int Widget_Quantum_Light_ImageButton = 16974505; // 0x10302a9 + field public static final int Widget_Quantum_Light_ListPopupWindow = 16974506; // 0x10302aa + field public static final int Widget_Quantum_Light_ListView = 16974507; // 0x10302ab + field public static final int Widget_Quantum_Light_ListView_DropDown = 16974508; // 0x10302ac + field public static final int Widget_Quantum_Light_MediaRouteButton = 16974509; // 0x10302ad + field public static final int Widget_Quantum_Light_PopupMenu = 16974510; // 0x10302ae + field public static final int Widget_Quantum_Light_PopupMenu_Overflow = 16974511; // 0x10302af + field public static final int Widget_Quantum_Light_PopupWindow = 16974512; // 0x10302b0 + field public static final int Widget_Quantum_Light_ProgressBar = 16974513; // 0x10302b1 + field public static final int Widget_Quantum_Light_ProgressBar_Horizontal = 16974514; // 0x10302b2 + field public static final int Widget_Quantum_Light_ProgressBar_Inverse = 16974515; // 0x10302b3 + field public static final int Widget_Quantum_Light_ProgressBar_Large = 16974516; // 0x10302b4 + field public static final int Widget_Quantum_Light_ProgressBar_Large_Inverse = 16974517; // 0x10302b5 + field public static final int Widget_Quantum_Light_ProgressBar_Small = 16974518; // 0x10302b6 + field public static final int Widget_Quantum_Light_ProgressBar_Small_Inverse = 16974519; // 0x10302b7 + field public static final int Widget_Quantum_Light_ProgressBar_Small_Title = 16974520; // 0x10302b8 + field public static final int Widget_Quantum_Light_RatingBar = 16974521; // 0x10302b9 + field public static final int Widget_Quantum_Light_RatingBar_Indicator = 16974522; // 0x10302ba + field public static final int Widget_Quantum_Light_RatingBar_Small = 16974523; // 0x10302bb + field public static final int Widget_Quantum_Light_ScrollView = 16974524; // 0x10302bc + field public static final int Widget_Quantum_Light_SeekBar = 16974525; // 0x10302bd + field public static final int Widget_Quantum_Light_SegmentedButton = 16974526; // 0x10302be + field public static final int Widget_Quantum_Light_Spinner = 16974528; // 0x10302c0 + field public static final int Widget_Quantum_Light_StackView = 16974527; // 0x10302bf + field public static final int Widget_Quantum_Light_Tab = 16974529; // 0x10302c1 + field public static final int Widget_Quantum_Light_TabWidget = 16974530; // 0x10302c2 + field public static final int Widget_Quantum_Light_TextView = 16974531; // 0x10302c3 + field public static final int Widget_Quantum_Light_TextView_SpinnerItem = 16974532; // 0x10302c4 + field public static final int Widget_Quantum_Light_WebTextView = 16974533; // 0x10302c5 + field public static final int Widget_Quantum_Light_WebView = 16974534; // 0x10302c6 field public static final int Widget_Quantum_ListPopupWindow = 16974448; // 0x1030270 field public static final int Widget_Quantum_ListView = 16974449; // 0x1030271 field public static final int Widget_Quantum_ListView_DropDown = 16974450; // 0x1030272 @@ -3251,6 +3257,7 @@ package android.app { method public boolean navigateUpToFromChild(android.app.Activity, android.content.Intent); method public void onActionModeFinished(android.view.ActionMode); method public void onActionModeStarted(android.view.ActionMode); + method protected void onActivityReenter(int, android.content.Intent); method protected void onActivityResult(int, int, android.content.Intent); method public void onAttachFragment(android.app.Fragment); method public void onAttachedToWindow(); @@ -3329,7 +3336,6 @@ package android.app { method public final boolean requestWindowFeature(int); method public final void runOnUiThread(java.lang.Runnable); method public void setActionBar(android.widget.Toolbar); - method public void setActivityTransitionListener(android.app.ActivityOptions.ActivityTransitionListener); method public void setContentTransitionManager(android.transition.TransitionManager); method public void setContentView(int); method public void setContentView(android.view.View); @@ -3351,6 +3357,7 @@ package android.app { method public final void setResult(int); method public final void setResult(int, android.content.Intent); method public final void setSecondaryProgress(int); + method public void setSharedElementListener(android.app.SharedElementListener); method public void setTitle(java.lang.CharSequence); method public void setTitle(int); method public deprecated void setTitleColor(int); @@ -3403,6 +3410,7 @@ package android.app { public class ActivityManager { method public boolean clearApplicationUserData(); method public void dumpPackageState(java.io.FileDescriptor, java.lang.String); + method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks(); method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo(); method public int getLargeMemoryClass(); method public int getLauncherLargeIconDensity(); @@ -3431,6 +3439,11 @@ package android.app { field public static final int RECENT_WITH_EXCLUDED = 1; // 0x1 } + public static class ActivityManager.AppTask { + method public void finishAndRemoveTask(); + method public android.app.ActivityManager.RecentTaskInfo getTaskInfo(); + } + public static class ActivityManager.MemoryInfo implements android.os.Parcelable { ctor public ActivityManager.MemoryInfo(); method public int describeContents(); @@ -3564,28 +3577,13 @@ package android.app { public class ActivityOptions { method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int); method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int); - method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.view.Window, android.view.View, java.lang.String); - method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.view.Window, android.app.ActivityOptions.ActivityTransitionListener); + method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String); + method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View, java.lang.String>...); method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int); method public android.os.Bundle toBundle(); method public void update(android.app.ActivityOptions); } - public static class ActivityOptions.ActivityTransitionListener { - ctor public ActivityOptions.ActivityTransitionListener(); - method public android.util.Pair<android.view.View, java.lang.String>[] getSharedElementsMapping(); - method public boolean handleRejectedSharedElements(java.util.List<android.view.View>); - method public void onCaptureSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>); - method public void onCaptureSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>); - method public void onEnterReady(); - method public void onExitTransitionComplete(); - method public void onRemoteExitComplete(); - method public void onSharedElementExitTransitionComplete(); - method public void onSharedElementTransferred(java.util.List<java.lang.String>, java.util.List<android.view.View>); - method public void onStartEnterTransition(java.util.List<java.lang.String>, java.util.List<android.view.View>); - method public void onStartExitTransition(java.util.List<java.lang.String>, java.util.List<android.view.View>); - } - public class AlarmManager { method public void cancel(android.app.PendingIntent); method public void set(int, long, android.app.PendingIntent); @@ -4104,11 +4102,10 @@ package android.app { field public static final android.os.Parcelable.ClassLoaderCreator CREATOR; } - public class FragmentBreadCrumbs extends android.view.ViewGroup implements android.app.FragmentManager.OnBackStackChangedListener { + public deprecated class FragmentBreadCrumbs extends android.view.ViewGroup implements android.app.FragmentManager.OnBackStackChangedListener { ctor public FragmentBreadCrumbs(android.content.Context); ctor public FragmentBreadCrumbs(android.content.Context, android.util.AttributeSet); ctor public FragmentBreadCrumbs(android.content.Context, android.util.AttributeSet, int); - ctor public FragmentBreadCrumbs(android.content.Context, android.util.AttributeSet, int, int); method public void onBackStackChanged(); method protected void onLayout(boolean, int, int, int, int); method public void setActivity(android.app.Activity); @@ -4518,6 +4515,7 @@ package android.app { ctor public Notification.Action(int, java.lang.CharSequence, android.app.PendingIntent); method public android.app.Notification.Action clone(); method public int describeContents(); + method public android.os.Bundle getExtras(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public android.app.PendingIntent actionIntent; @@ -4525,6 +4523,14 @@ package android.app { field public java.lang.CharSequence title; } + public static class Notification.Action.Builder { + ctor public Notification.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent); + ctor public Notification.Action.Builder(android.app.Notification.Action); + method public android.app.Notification.Action.Builder addExtras(android.os.Bundle); + method public android.app.Notification.Action build(); + method public android.os.Bundle getExtras(); + } + public static class Notification.BigPictureStyle extends android.app.Notification.Style { ctor public Notification.BigPictureStyle(); ctor public Notification.BigPictureStyle(android.app.Notification.Builder); @@ -4545,6 +4551,7 @@ package android.app { public static class Notification.Builder { ctor public Notification.Builder(android.content.Context); method public android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent); + method public android.app.Notification.Builder addAction(android.app.Notification.Action); method public android.app.Notification.Builder addExtras(android.os.Bundle); method public android.app.Notification.Builder addPerson(java.lang.String); method public android.app.Notification build(); @@ -4809,6 +4816,14 @@ package android.app { field public static final int START_STICKY_COMPATIBILITY = 0; // 0x0 } + public class SharedElementListener { + ctor public SharedElementListener(); + method public void handleRejectedSharedElements(java.util.List<android.view.View>); + method public void remapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>); + method public void setSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>); + method public void setSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>); + } + public deprecated class TabActivity extends android.app.ActivityGroup { ctor public TabActivity(); method public android.widget.TabHost getTabHost(); @@ -10083,7 +10098,7 @@ package android.graphics { enum_constant public static final android.graphics.Interpolator.Result NORMAL; } - public class LayerRasterizer extends android.graphics.Rasterizer { + public deprecated class LayerRasterizer extends android.graphics.Rasterizer { ctor public LayerRasterizer(); method public void addLayer(android.graphics.Paint, float, float); method public void addLayer(android.graphics.Paint); @@ -10111,6 +10126,7 @@ package android.graphics { ctor public Matrix(android.graphics.Matrix); method public void getValues(float[]); method public boolean invert(android.graphics.Matrix); + method public boolean isAffine(); method public boolean isIdentity(); method public void mapPoints(float[], int, float[], int, int); method public void mapPoints(float[], float[]); @@ -10208,7 +10224,9 @@ package android.graphics { public final class Outline { ctor public Outline(); ctor public Outline(android.graphics.Outline); + method public boolean canClip(); method public boolean isValid(); + method public void reset(); method public void set(android.graphics.Outline); method public void setConvexPath(android.graphics.Path); method public void setOval(int, int, int, int); @@ -10242,7 +10260,7 @@ package android.graphics { method public int getHinting(); method public android.graphics.MaskFilter getMaskFilter(); method public android.graphics.PathEffect getPathEffect(); - method public android.graphics.Rasterizer getRasterizer(); + method public deprecated android.graphics.Rasterizer getRasterizer(); method public android.graphics.Shader getShader(); method public android.graphics.Paint.Cap getStrokeCap(); method public android.graphics.Paint.Join getStrokeJoin(); @@ -10293,7 +10311,7 @@ package android.graphics { method public void setLinearText(boolean); method public android.graphics.MaskFilter setMaskFilter(android.graphics.MaskFilter); method public android.graphics.PathEffect setPathEffect(android.graphics.PathEffect); - method public android.graphics.Rasterizer setRasterizer(android.graphics.Rasterizer); + method public deprecated android.graphics.Rasterizer setRasterizer(android.graphics.Rasterizer); method public android.graphics.Shader setShader(android.graphics.Shader); method public void setShadowLayer(float, float, float, int); method public void setStrikeThruText(boolean); @@ -10602,7 +10620,7 @@ package android.graphics { ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode); } - public class Rasterizer { + public deprecated class Rasterizer { ctor public Rasterizer(); } @@ -11350,6 +11368,24 @@ package android.graphics.pdf { method public android.graphics.pdf.PdfDocument.PageInfo.Builder setContentRect(android.graphics.Rect); } + public final class PdfRenderer implements java.lang.AutoCloseable { + ctor public PdfRenderer(android.os.ParcelFileDescriptor) throws java.io.IOException; + method public void close(); + method public void closePage(android.graphics.pdf.PdfRenderer.Page); + method public int getPageCount(); + method public android.graphics.pdf.PdfRenderer.Page openPage(int); + method public boolean shouldScaleForPrinting(); + } + + public final class PdfRenderer.Page { + method public int getHeight(); + method public int getIndex(); + method public int getWidth(); + method public void render(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Matrix, int); + field public static final int RENDER_MODE_FOR_DISPLAY = 1; // 0x1 + field public static final int RENDER_MODE_FOR_PRINT = 2; // 0x2 + } + } package android.hardware { @@ -11853,16 +11889,8 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_MAX_NUM_OUTPUT_STREAMS; field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PARTIAL_RESULT_COUNT; field public static final android.hardware.camera2.CameraMetadata.Key REQUEST_PIPELINE_MAX_DEPTH; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_FORMATS; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_JPEG_MIN_DURATIONS; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_JPEG_SIZES; field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_MAX_DIGITAL_ZOOM; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_MIN_FRAME_DURATIONS; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_PROCESSED_SIZES; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_STALL_DURATIONS; - field public static final android.hardware.camera2.CameraMetadata.Key SCALER_AVAILABLE_STREAM_CONFIGURATIONS; + field public static final android.hardware.camera2.CameraMetadata.Key SCALER_STREAM_CONFIGURATION_MAP; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_AVAILABLE_TEST_PATTERN_MODES; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_BASE_GAIN_FACTOR; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_BLACK_LEVEL_PATTERN; @@ -12072,8 +12100,6 @@ package android.hardware.camera2 { field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5; // 0x5 field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 2; // 0x2 field public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4; // 0x4 - field public static final int SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT = 1; // 0x1 - field public static final int SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT = 0; // 0x0 field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3 field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG = 2; // 0x2 field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG = 1; // 0x1 @@ -12354,6 +12380,22 @@ package android.hardware.camera2 { method public final int getWidth(); } + public final class TonemapCurve { + method public void copyColorCurve(int, float[], int); + method public android.graphics.PointF getPoint(int, int); + method public int getPointCount(int); + field public static final int CHANNEL_BLUE = 2; // 0x2 + field public static final int CHANNEL_GREEN = 1; // 0x1 + field public static final int CHANNEL_RED = 0; // 0x0 + field public static final float LEVEL_BLACK = 0.0f; + field public static final float LEVEL_WHITE = 1.0f; + field public static final int POINT_SIZE = 2; // 0x2 + } + +} + +package android.hardware.camera2.params { + public final class StreamConfigurationMap { method public final int[] getOutputFormats(); method public long getOutputMinFrameDuration(int, android.util.Size); @@ -12367,18 +12409,6 @@ package android.hardware.camera2 { method public boolean isOutputSupportedFor(android.view.Surface); } - public final class TonemapCurve { - method public void copyColorCurve(int, float[], int); - method public android.graphics.PointF getPoint(int, int); - method public int getPointCount(int); - field public static final int CHANNEL_BLUE = 2; // 0x2 - field public static final int CHANNEL_GREEN = 1; // 0x1 - field public static final int CHANNEL_RED = 0; // 0x0 - field public static final float LEVEL_BLACK = 0.0f; - field public static final float LEVEL_WHITE = 1.0f; - field public static final int POINT_SIZE = 2; // 0x2 - } - } package android.hardware.display { @@ -12456,16 +12486,17 @@ package android.hardware.hdmi { field public static final int MESSAGE_FEATURE_ABORT = 0; // 0x0 field public static final int MESSAGE_GET_CEC_VERSION = 159; // 0x9f field public static final int MESSAGE_GET_MENU_LANGUAGE = 145; // 0x91 - field public static final int MESSAGE_GET_OSD_NAME = 70; // 0x46 field public static final int MESSAGE_GIVE_AUDIO_STATUS = 113; // 0x71 field public static final int MESSAGE_GIVE_DECK_STATUS = 26; // 0x1a field public static final int MESSAGE_GIVE_DEVICE_POWER_STATUS = 143; // 0x8f field public static final int MESSAGE_GIVE_DEVICE_VENDOR_ID = 140; // 0x8c + field public static final int MESSAGE_GIVE_OSD_NAME = 70; // 0x46 field public static final int MESSAGE_GIVE_PHYSICAL_ADDRESS = 131; // 0x83 field public static final int MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 125; // 0x7d field public static final int MESSAGE_GIVE_TUNER_DEVICE_STATUS = 8; // 0x8 field public static final int MESSAGE_IMAGE_VIEW_ON = 4; // 0x4 field public static final int MESSAGE_INACTIVE_SOURCE = 157; // 0x9d + field public static final int MESSAGE_INITIATE_ARC = 192; // 0xc0 field public static final int MESSAGE_MENU_REQUEST = 141; // 0x8d field public static final int MESSAGE_MENU_STATUS = 142; // 0x8e field public static final int MESSAGE_PLAY = 65; // 0x41 @@ -12473,10 +12504,14 @@ package android.hardware.hdmi { field public static final int MESSAGE_RECORD_ON = 9; // 0x9 field public static final int MESSAGE_RECORD_STATUS = 10; // 0xa field public static final int MESSAGE_RECORD_TV_SCREEN = 15; // 0xf + field public static final int MESSAGE_REPORT_ARC_INITIATED = 193; // 0xc1 + field public static final int MESSAGE_REPORT_ARC_TERMINATED = 194; // 0xc2 field public static final int MESSAGE_REPORT_AUDIO_STATUS = 122; // 0x7a field public static final int MESSAGE_REPORT_PHYSICAL_ADDRESS = 132; // 0x84 field public static final int MESSAGE_REPORT_POWER_STATUS = 144; // 0x90 field public static final int MESSAGE_REQUEST_ACTIVE_SOURCE = 133; // 0x85 + field public static final int MESSAGE_REQUEST_ARC_INITIATION = 195; // 0xc3 + field public static final int MESSAGE_REQUEST_ARC_TERMINATION = 196; // 0xc4 field public static final int MESSAGE_ROUTING_CHANGE = 128; // 0x80 field public static final int MESSAGE_ROUTING_INFORMATION = 129; // 0x81 field public static final int MESSAGE_SELECT_ANALOG_SERVICE = 146; // 0x92 @@ -12494,6 +12529,7 @@ package android.hardware.hdmi { field public static final int MESSAGE_STANDBY = 54; // 0x36 field public static final int MESSAGE_SYSTEM_AUDIO_MODE_REQUEST = 112; // 0x70 field public static final int MESSAGE_SYSTEM_AUDIO_MODE_STATUS = 126; // 0x7e + field public static final int MESSAGE_TERMINATE_ARC = 197; // 0xc5 field public static final int MESSAGE_TEXT_VIEW_ON = 13; // 0xd field public static final int MESSAGE_TIMER_CLEARED_STATUS = 67; // 0x43 field public static final int MESSAGE_TIMER_STATUS = 53; // 0x35 @@ -15547,7 +15583,7 @@ package android.media.session { method public void addCallback(android.media.session.Session.Callback); method public void addCallback(android.media.session.Session.Callback, android.os.Handler); method public void connect(android.media.session.RouteInfo, android.media.session.RouteOptions); - method public void disconnect(android.media.session.RouteInfo); + method public void disconnect(); method public android.media.session.SessionToken getSessionToken(); method public android.media.session.TransportPerformer getTransportPerformer(); method public boolean isActive(); @@ -15557,6 +15593,11 @@ package android.media.session { method public void setActive(boolean); method public void setFlags(int); method public void setRouteOptions(java.util.List<android.media.session.RouteOptions>); + field public static final int DISCONNECT_REASON_PROVIDER_DISCONNECTED = 2; // 0x2 + field public static final int DISCONNECT_REASON_ROUTE_CHANGED = 3; // 0x3 + field public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5; // 0x5 + field public static final int DISCONNECT_REASON_SESSION_DISCONNECTED = 4; // 0x4 + field public static final int DISCONNECT_REASON_USER_STOPPING = 1; // 0x1 field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } @@ -19553,6 +19594,7 @@ package android.os { public class BatteryProperty implements android.os.Parcelable { method public int describeContents(); method public int getInt(); + method public long getLong(); method public void readFromParcel(android.os.Parcel); method public void writeToParcel(android.os.Parcel, int); field public static final int CAPACITY = 4; // 0x4 @@ -19560,6 +19602,7 @@ package android.os { field public static final android.os.Parcelable.Creator CREATOR; field public static final int CURRENT_AVERAGE = 3; // 0x3 field public static final int CURRENT_NOW = 2; // 0x2 + field public static final int ENERGY_COUNTER = 4; // 0x4 } public class Binder implements android.os.IBinder { @@ -25294,10 +25337,10 @@ package android.service.trust { public class TrustAgentService extends android.app.Service { ctor public TrustAgentService(); - method protected final void enableTrust(java.lang.String, long, boolean); + method public final void grantTrust(java.lang.CharSequence, long, boolean); method public final android.os.IBinder onBind(android.content.Intent); - method protected void onUnlockAttempt(boolean); - method protected final void revokeTrust(); + method public void onUnlockAttempt(boolean); + method public final void revokeTrust(); field public static final java.lang.String SERVICE_INTERFACE = "android.service.trust.TrustAgentService"; field public static final java.lang.String TRUST_AGENT_META_DATA = "android.service.trust.trustagent"; } @@ -29351,8 +29394,13 @@ package android.transition { method public android.transition.Transition setDuration(long); method public void setEpicenterCallback(android.transition.Transition.EpicenterCallback); method public android.transition.Transition setInterpolator(android.animation.TimeInterpolator); + method public void setMatchOrder(int...); method public void setPropagation(android.transition.TransitionPropagation); method public android.transition.Transition setStartDelay(long); + field public static final int MATCH_ID = 3; // 0x3 + field public static final int MATCH_INSTANCE = 1; // 0x1 + field public static final int MATCH_ITEM_ID = 4; // 0x4 + field public static final int MATCH_VIEW_NAME = 2; // 0x2 } public static abstract class Transition.EpicenterCallback { @@ -32548,8 +32596,10 @@ package android.view { method protected final int getForcedWindowFlags(); method public abstract android.view.LayoutInflater getLayoutInflater(); method protected final int getLocalFeatures(); + method public abstract int getNavigationBarColor(); method public android.transition.Transition getSharedElementEnterTransition(); method public android.transition.Transition getSharedElementExitTransition(); + method public abstract int getStatusBarColor(); method public android.transition.TransitionManager getTransitionManager(); method public abstract int getVolumeControlStream(); method public android.view.WindowManager getWindowManager(); @@ -32601,9 +32651,11 @@ package android.view { method public void setLayout(int, int); method public void setLocalFocus(boolean, boolean); method public void setLogo(int); + method public abstract void setNavigationBarColor(int); method public void setSharedElementEnterTransition(android.transition.Transition); method public void setSharedElementExitTransition(android.transition.Transition); method public void setSoftInputMode(int); + method public abstract void setStatusBarColor(int); method public abstract void setTitle(java.lang.CharSequence); method public abstract deprecated void setTitleColor(int); method public void setTransitionManager(android.transition.TransitionManager); @@ -32761,6 +32813,7 @@ package android.view { field public static final int FLAG_DIM_BEHIND = 2; // 0x2 field public static final int FLAG_DISMISS_KEYGUARD = 4194304; // 0x400000 field public static final deprecated int FLAG_DITHER = 4096; // 0x1000 + field public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = -2147483648; // 0x80000000 field public static final int FLAG_FORCE_NOT_FULLSCREEN = 2048; // 0x800 field public static final int FLAG_FULLSCREEN = 1024; // 0x400 field public static final int FLAG_HARDWARE_ACCELERATED = 16777216; // 0x1000000 @@ -32951,7 +33004,8 @@ package android.view.accessibility { } public class AccessibilityNodeInfo implements android.os.Parcelable { - method public void addAction(int); + method public void addAction(android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction); + method public deprecated void addAction(int); method public void addChild(android.view.View); method public void addChild(android.view.View, int); method public boolean canOpenPopup(); @@ -32960,7 +33014,8 @@ package android.view.accessibility { method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(java.lang.String); method public android.view.accessibility.AccessibilityNodeInfo findFocus(int); method public android.view.accessibility.AccessibilityNodeInfo focusSearch(int); - method public int getActions(); + method public java.util.List<android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction> getActionList(); + method public deprecated int getActions(); method public void getBoundsInParent(android.graphics.Rect); method public void getBoundsInScreen(android.graphics.Rect); method public android.view.accessibility.AccessibilityNodeInfo getChild(int); @@ -33008,7 +33063,8 @@ package android.view.accessibility { method public boolean performAction(int, android.os.Bundle); method public void recycle(); method public boolean refresh(); - method public void removeAction(int); + method public deprecated void removeAction(int); + method public boolean removeAction(android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction); method public boolean removeChild(android.view.View); method public boolean removeChild(android.view.View, int); method public void setAccessibilityFocused(boolean); @@ -33089,6 +33145,34 @@ package android.view.accessibility { field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2 } + public static final class AccessibilityNodeInfo.AccessibilityAction { + ctor public AccessibilityNodeInfo.AccessibilityAction(int, java.lang.CharSequence); + method public int getId(); + method public java.lang.CharSequence getLabel(); + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_ACCESSIBILITY_FOCUS; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_CLEAR_ACCESSIBILITY_FOCUS; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_CLEAR_FOCUS; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_CLEAR_SELECTION; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_CLICK; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_COLLAPSE; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_COPY; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_CUT; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_LONG_CLICK; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_NEXT_HTML_ELEMENT; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PASTE; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_BACKWARD; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SCROLL_FORWARD; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION; + field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT; + } + public static final class AccessibilityNodeInfo.CollectionInfo { method public int getColumnCount(); method public int getRowCount(); @@ -33568,9 +33652,9 @@ package android.view.inputmethod { public final class CursorAnchorInfo implements android.os.Parcelable { ctor public CursorAnchorInfo(android.os.Parcel); method public int describeContents(); - method public int getCandidatesEnd(); - method public int getCandidatesStart(); method public android.graphics.RectF getCharacterRect(int); + method public java.lang.String getComposingText(); + method public int getComposingTextStart(); method public float getInsertionMarkerBaseline(); method public float getInsertionMarkerBottom(); method public float getInsertionMarkerHorizontal(); @@ -33587,7 +33671,7 @@ package android.view.inputmethod { method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder addCharacterRect(int, float, float, float, float); method public android.view.inputmethod.CursorAnchorInfo build(); method public void reset(); - method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setCandidateRange(int, int); + 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); @@ -36889,6 +36973,10 @@ package android.widget { ctor public Toolbar(android.content.Context, android.util.AttributeSet); ctor public Toolbar(android.content.Context, android.util.AttributeSet, int); ctor public Toolbar(android.content.Context, android.util.AttributeSet, int, int); + method public int getContentInsetEnd(); + method public int getContentInsetLeft(); + method public int getContentInsetRight(); + method public int getContentInsetStart(); method public android.graphics.drawable.Drawable getLogo(); method public java.lang.CharSequence getLogoDescription(); method public android.view.Menu getMenu(); @@ -36897,6 +36985,8 @@ package android.widget { method public java.lang.CharSequence getTitle(); method public void inflateMenu(int); method protected void onLayout(boolean, int, int, int, int); + method public void setContentInsetsAbsolute(int, int); + method public void setContentInsetsRelative(int, int); method public void setLogo(int); method public void setLogo(android.graphics.drawable.Drawable); method public void setLogoDescription(int); diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk index 7c25354..74a2f7b 100644 --- a/cmds/app_process/Android.mk +++ b/cmds/app_process/Android.mk @@ -1,6 +1,5 @@ LOCAL_PATH:= $(call my-dir) -# 32-bit app_process include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ @@ -15,10 +14,14 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= app_process LOCAL_MULTILIB := both -LOCAL_MODULE_STEM_32 := app_process +LOCAL_MODULE_STEM_32 := app_process32 LOCAL_MODULE_STEM_64 := app_process64 include $(BUILD_EXECUTABLE) +# Create a symlink from app_process to app_process32 or 64 +# depending on the target configuration. +include $(BUILD_SYSTEM)/executable_prefer_symlink.mk + # Build a variant of app_process binary linked with ASan runtime. # ARM-only at the moment. ifeq ($(TARGET_ARCH),arm) diff --git a/core/java/android/alsa/AlsaCardsParser.java b/core/java/android/alsa/AlsaCardsParser.java index f9af979..8b44881 100644 --- a/core/java/android/alsa/AlsaCardsParser.java +++ b/core/java/android/alsa/AlsaCardsParser.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.alsascan; +package android.alsa; import android.util.Slog; import java.io.BufferedReader; diff --git a/core/java/android/alsa/AlsaDevicesParser.java b/core/java/android/alsa/AlsaDevicesParser.java index 094c8a2..82cc1ae 100644 --- a/core/java/android/alsa/AlsaDevicesParser.java +++ b/core/java/android/alsa/AlsaDevicesParser.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.alsascan; +package android.alsa; import android.util.Slog; import java.io.BufferedReader; diff --git a/core/java/android/alsa/LineTokenizer.java b/core/java/android/alsa/LineTokenizer.java index c138fc5..78c91b5 100644 --- a/core/java/android/alsa/LineTokenizer.java +++ b/core/java/android/alsa/LineTokenizer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.alsascan; +package android.alsa; /** * @hide diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4a30b05..36c36a8 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -777,8 +777,9 @@ public class Activity extends ContextThemeWrapper private Thread mUiThread; final Handler mHandler = new Handler(); - private ActivityOptions mCalledActivityOptions; - private EnterTransitionCoordinator mEnterTransitionCoordinator; + + private ActivityTransitionState mActivityTransitionState = new ActivityTransitionState(); + SharedElementListener mTransitionListener = new SharedElementListener(); /** Return the intent that started this activity. */ public Intent getIntent() { @@ -1100,9 +1101,6 @@ public class Activity extends ContextThemeWrapper mTitleReady = true; onTitleChanged(getTitle(), getTitleColor()); } - if (mEnterTransitionCoordinator != null) { - mEnterTransitionCoordinator.readyToEnter(); - } mCalled = true; } @@ -1149,12 +1147,6 @@ public class Activity extends ContextThemeWrapper } getApplication().dispatchActivityStarted(this); - - final ActivityOptions activityOptions = getActivityOptions(); - if (activityOptions != null && - activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { - mEnterTransitionCoordinator = activityOptions.createEnterActivityTransition(this); - } } /** @@ -1204,7 +1196,6 @@ public class Activity extends ContextThemeWrapper protected void onResume() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this); getApplication().dispatchActivityResumed(this); - mCalledActivityOptions = null; mCalled = true; } @@ -1279,6 +1270,7 @@ public class Activity extends ContextThemeWrapper final void performSaveInstanceState(Bundle outState) { onSaveInstanceState(outState); saveManagedDialogs(outState); + mActivityTransitionState.saveState(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState); } @@ -1549,10 +1541,7 @@ public class Activity extends ContextThemeWrapper protected void onStop() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); - if (mCalledActivityOptions != null) { - mCalledActivityOptions.dispatchActivityStopped(); - mCalledActivityOptions = null; - } + mActivityTransitionState.onStop(); getApplication().dispatchActivityStopped(this); mTranslucentCallback = null; mCalled = true; @@ -3650,7 +3639,7 @@ public class Activity extends ContextThemeWrapper public void startActivityForResult(Intent intent, int requestCode) { Bundle options = null; if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) { - options = ActivityOptions.makeSceneTransitionAnimation(mWindow, null).toBundle(); + options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle(); } startActivityForResult(intent, requestCode, options); } @@ -3691,9 +3680,7 @@ public class Activity extends ContextThemeWrapper */ public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { if (options != null) { - ActivityOptions activityOptions = new ActivityOptions(options); - activityOptions.dispatchStartExit(); - mCalledActivityOptions = activityOptions; + mActivityTransitionState.startExitOutTransition(this, options); } if (mParent == null) { Instrumentation.ActivityResult ar = @@ -4559,13 +4546,10 @@ public class Activity extends ContextThemeWrapper * to reverse its exit Transition. When the exit Transition completes, * {@link #finish()} is called. If no entry Transition was used, finish() is called * immediately and the Activity exit Transition is run. - * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window, - * android.app.ActivityOptions.ActivityTransitionListener) + * @see android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, android.util.Pair[]) */ public void finishWithTransition() { - if (mEnterTransitionCoordinator != null) { - mEnterTransitionCoordinator.startExit(); - } else { + if (!mActivityTransitionState.startExitBackTransition(this)) { finish(); } } @@ -4643,6 +4627,27 @@ public class Activity extends ContextThemeWrapper } /** + * Called when an activity you launched with an activity transition exposes this + * Activity through a returning activity transition, giving you the resultCode + * and any additional data from it. This method will only be called if the activity + * set a result code other than {@link #RESULT_CANCELED} and it supports activity + * transitions with {@link Window#FEATURE_CONTENT_TRANSITIONS}. + * + * <p>The purpose of this function is to let the called Activity send a hint about + * its state so that this underlying Activity can prepare to be exposed. A call to + * this method does not guarantee that the called Activity has or will be exiting soon. + * It only indicates that it will expose this Activity's Window and it has + * some data to pass to prepare it.</p> + * + * @param resultCode The integer result code returned by the child activity + * through its setResult(). + * @param data An Intent, which can return result data to the caller + * (various data can be attached to Intent "extras"). + */ + protected void onActivityReenter(int resultCode, Intent data) { + } + + /** * Create a new PendingIntent object which you can hand to others * for them to use to send result data back to your * {@link #onActivityResult} callback. The created object will be either @@ -5246,7 +5251,8 @@ public class Activity extends ContextThemeWrapper * This call has no effect on non-translucent activities or on activities with the * {@link android.R.attr#windowIsFloating} attribute. * - * @see #convertToTranslucent(TranslucentConversionListener) + * @see #convertToTranslucent(android.app.Activity.TranslucentConversionListener, + * ActivityOptions) * @see TranslucentConversionListener * * @hide @@ -5544,18 +5550,18 @@ public class Activity extends ContextThemeWrapper } /** - * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window, - * android.app.ActivityOptions.ActivityTransitionListener)} was used to start an Activity, - * the Window will be triggered to enter with a Transition. <code>listener</code> allows - * The Activity to listen to events of the entering transition and control the mapping of - * shared elements. This requires {@link Window#FEATURE_CONTENT_TRANSITIONS}. + * When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, + * android.view.View, String)} was used to start an Activity, <var>listener</var> + * will be called to handle shared elements. This requires + * {@link Window#FEATURE_CONTENT_TRANSITIONS}. * - * @param listener Used to listen to events in the entering transition. + * @param listener Used to manipulate how shared element transitions function. */ - public void setActivityTransitionListener(ActivityOptions.ActivityTransitionListener listener) { - if (mEnterTransitionCoordinator != null) { - mEnterTransitionCoordinator.setActivityTransitionListener(listener); + public void setSharedElementListener(SharedElementListener listener) { + if (listener == null) { + listener = new SharedElementListener(); } + mTransitionListener = listener; } // ------------------ Internal API ------------------ @@ -5621,19 +5627,23 @@ public class Activity extends ContextThemeWrapper mVisibleFromClient = !mWindow.getWindowStyle().getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); mFragments.dispatchActivityCreated(); + mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); } final void performCreate(Bundle icicle) { onCreate(icicle); + mActivityTransitionState.readState(icicle); performCreateCommon(); } final void performCreate(Bundle icicle, PersistableBundle persistentState) { onCreate(icicle, persistentState); + mActivityTransitionState.readState(icicle); performCreateCommon(); } final void performStart() { + mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions()); mFragments.noteStateNotSaved(); mCalled = false; mFragments.execPendingActions(); @@ -5656,6 +5666,7 @@ public class Activity extends ContextThemeWrapper lm.doReportStart(); } } + mActivityTransitionState.enterReady(this); } final void performRestart() { diff --git a/core/java/android/app/ActivityManager.aidl b/core/java/android/app/ActivityManager.aidl new file mode 100644 index 0000000..92350da --- /dev/null +++ b/core/java/android/app/ActivityManager.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.app; + +parcelable ActivityManager.RecentTaskInfo; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 044727d..1d05320 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -52,6 +52,7 @@ import android.util.Slog; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -474,7 +475,7 @@ public class ActivityManager { } /** - * Information you can set and retrieve about the current activity within Recents. + * Information you can set and retrieve about the current activity within the recent task list. */ public static class RecentsActivityValues implements Parcelable { public CharSequence label; @@ -879,7 +880,29 @@ public class ActivityManager { readFromParcel(source); } } - + + /** + * Get the list of tasks associated with the calling application. + * + * @return The list of tasks associated with the application making this call. + * @throws SecurityException + */ + public List<ActivityManager.AppTask> getAppTasks() { + ArrayList<AppTask> tasks = new ArrayList<AppTask>(); + List<IAppTask> appTasks; + try { + appTasks = ActivityManagerNative.getDefault().getAppTasks(); + } catch (RemoteException e) { + // System dead, we will be dead too soon! + return null; + } + int numAppTasks = appTasks.size(); + for (int i = 0; i < numAppTasks; i++) { + tasks.add(new AppTask(appTasks.get(i))); + } + return tasks; + } + /** * Return a list of the tasks that are currently running, with * the most recent being first and older ones after in order. Note that @@ -2382,4 +2405,42 @@ public class ActivityManager { return false; } } + + /** + * The AppTask allows you to manage your own application's tasks. + * See {@link android.app.ActivityManager#getAppTasks()} + */ + public static class AppTask { + private IAppTask mAppTaskImpl; + + /** @hide */ + public AppTask(IAppTask task) { + mAppTaskImpl = task; + } + + /** + * Finishes all activities in this task and removes it from the recent tasks list. + */ + public void finishAndRemoveTask() { + try { + mAppTaskImpl.finishAndRemoveTask(); + } catch (RemoteException e) { + Slog.e(TAG, "Invalid AppTask", e); + } + } + + /** + * Get the RecentTaskInfo associated with this task. + * + * @return The RecentTaskInfo for this task, or null if the task no longer exists. + */ + public RecentTaskInfo getTaskInfo() { + try { + return mAppTaskImpl.getTaskInfo(); + } catch (RemoteException e) { + Slog.e(TAG, "Invalid AppTask", e); + return null; + } + } + } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 2f924d3..e704a1c 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -506,6 +506,20 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_APP_TASKS_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + List<IAppTask> list = getAppTasks(); + reply.writeNoException(); + int N = list != null ? list.size() : -1; + reply.writeInt(N); + int i; + for (i=0; i<N; i++) { + IAppTask task = list.get(i); + reply.writeStrongBinder(task.asBinder()); + } + return true; + } + case GET_TASKS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int maxNum = data.readInt(); @@ -2683,6 +2697,26 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return res; } + public List<IAppTask> getAppTasks() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_APP_TASKS_TRANSACTION, data, reply, 0); + reply.readException(); + ArrayList<IAppTask> list = null; + int N = reply.readInt(); + if (N >= 0) { + list = new ArrayList<IAppTask>(); + while (N > 0) { + IAppTask task = IAppTask.Stub.asInterface(reply.readStrongBinder()); + list.add(task); + N--; + } + } + data.recycle(); + reply.recycle(); + return list; + } public List getTasks(int maxNum, int flags) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -2698,7 +2732,7 @@ class ActivityManagerProxy implements IActivityManager while (N > 0) { ActivityManager.RunningTaskInfo info = ActivityManager.RunningTaskInfo.CREATOR - .createFromParcel(reply); + .createFromParcel(reply); list.add(info); N--; } diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 692efd7..a057c3e 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -17,18 +17,18 @@ package android.app; import android.content.Context; +import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; import android.os.RemoteException; import android.os.ResultReceiver; -import android.transition.Transition; -import android.util.ArrayMap; import android.util.Pair; import android.view.View; import android.view.Window; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -108,6 +108,12 @@ public class ActivityOptions { private static final String KEY_TRANSITION_COMPLETE_LISTENER = "android:transitionCompleteListener"; + private static final String KEY_TRANSITION_IS_RETURNING = "android:transitionIsReturning"; + private static final String KEY_TRANSITION_SHARED_ELEMENTS = "android:sharedElementNames"; + private static final String KEY_LOCAL_SHARED_ELEMENTS = "android:localSharedElementNames"; + private static final String KEY_RESULT_DATA = "android:resultData"; + private static final String KEY_RESULT_CODE = "android:resultCode"; + /** @hide */ public static final int ANIM_NONE = 0; /** @hide */ @@ -131,7 +137,12 @@ public class ActivityOptions { private int mStartWidth; private int mStartHeight; private IRemoteCallback mAnimationStartedListener; - private ResultReceiver mExitReceiver; + private ResultReceiver mTransitionReceiver; + private boolean mIsReturning; + private ArrayList<String> mSharedElementNames; + private ArrayList<String> mLocalSharedElementNames; + private Intent mResultData; + private int mResultCode; /** * Create an ActivityOptions specifying a custom animation to run when @@ -334,7 +345,7 @@ public class ActivityOptions { * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be * enabled on the calling Activity to cause an exit transition. The same must be in * the called Activity to get an entering transition.</p> - * @param window The window containing shared elements. + * @param activity The Activity whose window contains the shared elements. * @param sharedElement The View to transition to the started Activity. sharedElement must * have a non-null sharedElementName. * @param sharedElementName The shared element name as used in the target Activity. This may @@ -344,40 +355,70 @@ public class ActivityOptions { * @see android.transition.Transition#setEpicenterCallback( * android.transition.Transition.EpicenterCallback) */ - public static ActivityOptions makeSceneTransitionAnimation(Window window, + public static ActivityOptions makeSceneTransitionAnimation(Activity activity, View sharedElement, String sharedElementName) { - return makeSceneTransitionAnimation(window, - new SharedElementMappingListener(sharedElement, sharedElementName)); + return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName)); } /** * Create an ActivityOptions to transition between Activities using cross-Activity scene * animations. This method carries the position of multiple shared elements to the started - * Activity. The position of the first element in the value returned from - * {@link android.app.ActivityOptions.ActivityTransitionListener#getSharedElementsMapping()} + * Activity. The position of the first element in sharedElements * will be used as the epicenter for the exit Transition. The position of the associated * shared element in the launched Activity will be the epicenter of its entering Transition. * * <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be * enabled on the calling Activity to cause an exit transition. The same must be in * the called Activity to get an entering transition.</p> - * @param window The window containing shared elements. - * @param listener The listener to use to monitor activity transition events. + * @param activity The Activity whose window contains the shared elements. + * @param sharedElements The names of the shared elements to transfer to the called + * Activity and their associated Views. The Views must each have + * a unique shared element name. * @return Returns a new ActivityOptions object that you can use to * supply these options as the options Bundle when starting an activity. * Returns null if the Window does not have {@link Window#FEATURE_CONTENT_TRANSITIONS}. * @see android.transition.Transition#setEpicenterCallback( * android.transition.Transition.EpicenterCallback) */ - public static ActivityOptions makeSceneTransitionAnimation(Window window, - ActivityTransitionListener listener) { - if (!window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) { + public static ActivityOptions makeSceneTransitionAnimation(Activity activity, + Pair<View, String>... sharedElements) { + if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) { return null; } ActivityOptions opts = new ActivityOptions(); opts.mAnimationType = ANIM_SCENE_TRANSITION; - ExitTransitionCoordinator exit = new ExitTransitionCoordinator(window, listener); - opts.mExitReceiver = exit; + + ArrayList<String> names = new ArrayList<String>(); + ArrayList<String> mappedNames = new ArrayList<String>(); + + 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()); + } + } + + ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names, + mappedNames, false); + opts.mTransitionReceiver = exit; + opts.mSharedElementNames = names; + opts.mLocalSharedElementNames = mappedNames; + opts.mIsReturning = false; + return opts; + } + + /** @hide */ + public static ActivityOptions makeSceneTransitionAnimation(Activity activity, + ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames, + int resultCode, Intent resultData) { + ActivityOptions opts = new ActivityOptions(); + opts.mAnimationType = ANIM_SCENE_TRANSITION; + opts.mSharedElementNames = sharedElementNames; + opts.mTransitionReceiver = exitCoordinator; + opts.mIsReturning = true; + opts.mResultCode = resultCode; + opts.mResultData = resultData; return opts; } @@ -413,7 +454,12 @@ public class ActivityOptions { break; case ANIM_SCENE_TRANSITION: - mExitReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER); + mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER); + mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false); + mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS); + mLocalSharedElementNames = opts.getStringArrayList(KEY_LOCAL_SHARED_ELEMENTS); + mResultData = opts.getParcelable(KEY_RESULT_DATA); + mResultCode = opts.getInt(KEY_RESULT_CODE); break; } } @@ -470,15 +516,15 @@ public class ActivityOptions { /** @hide */ public void dispatchActivityStopped() { - if (mExitReceiver != null) { - mExitReceiver.send(ActivityTransitionCoordinator.MSG_ACTIVITY_STOPPED, null); + if (mTransitionReceiver != null) { + mTransitionReceiver.send(ActivityTransitionCoordinator.MSG_ACTIVITY_STOPPED, null); } } /** @hide */ public void dispatchStartExit() { - if (mExitReceiver != null) { - mExitReceiver.send(ActivityTransitionCoordinator.MSG_START_EXIT_TRANSITION, null); + if (mTransitionReceiver != null) { + mTransitionReceiver.send(ActivityTransitionCoordinator.MSG_START_EXIT_TRANSITION, null); } } @@ -493,19 +539,37 @@ public class ActivityOptions { } /** @hide */ - public static void abort(Bundle options) { - if (options != null) { - (new ActivityOptions(options)).abort(); - } + public void setReturning() { + mIsReturning = true; } /** @hide */ - public EnterTransitionCoordinator createEnterActivityTransition(Activity activity) { - EnterTransitionCoordinator coordinator = null; - if (mAnimationType == ANIM_SCENE_TRANSITION) { - coordinator = new EnterTransitionCoordinator(activity, mExitReceiver); + public boolean isReturning() { + return mIsReturning; + } + + /** @hide */ + public ArrayList<String> getSharedElementNames() { + return mSharedElementNames; + } + + /** @hide */ + public ArrayList<String> getLocalSharedElementNames() { return mLocalSharedElementNames; } + + /** @hide */ + public ResultReceiver getResultReceiver() { return mTransitionReceiver; } + + /** @hide */ + public int getResultCode() { return mResultCode; } + + /** @hide */ + public Intent getResultData() { return mResultData; } + + /** @hide */ + public static void abort(Bundle options) { + if (options != null) { + (new ActivityOptions(options)).abort(); } - return coordinator; } /** @@ -517,7 +581,12 @@ public class ActivityOptions { if (otherOptions.mPackageName != null) { mPackageName = otherOptions.mPackageName; } - mExitReceiver = null; + mTransitionReceiver = null; + mSharedElementNames = null; + mLocalSharedElementNames = null; + mIsReturning = false; + mResultData = null; + mResultCode = 0; switch (otherOptions.mAnimationType) { case ANIM_CUSTOM: mAnimationType = otherOptions.mAnimationType; @@ -562,9 +631,14 @@ public class ActivityOptions { break; case ANIM_SCENE_TRANSITION: mAnimationType = otherOptions.mAnimationType; - mExitReceiver = otherOptions.mExitReceiver; + mTransitionReceiver = otherOptions.mTransitionReceiver; + mSharedElementNames = otherOptions.mSharedElementNames; + mLocalSharedElementNames = otherOptions.mLocalSharedElementNames; + mIsReturning = otherOptions.mIsReturning; mThumbnail = null; mAnimationStartedListener = null; + mResultData = otherOptions.mResultData; + mResultCode = otherOptions.mResultCode; break; } } @@ -608,9 +682,14 @@ public class ActivityOptions { break; case ANIM_SCENE_TRANSITION: b.putInt(KEY_ANIM_TYPE, mAnimationType); - if (mExitReceiver != null) { - b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mExitReceiver); + if (mTransitionReceiver != null) { + b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver); } + b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning); + b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames); + b.putStringArrayList(KEY_LOCAL_SHARED_ELEMENTS, mLocalSharedElementNames); + b.putParcelable(KEY_RESULT_DATA, mResultData); + b.putInt(KEY_RESULT_CODE, mResultCode); break; } return b; @@ -630,126 +709,4 @@ public class ActivityOptions { return null; } - /** - * Listener provided in - * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window, - * android.app.ActivityOptions.ActivityTransitionListener)} or in - * {@link android.app.Activity#setActivityTransitionListener( - * android.app.ActivityOptions.ActivityTransitionListener)} to monitor the Activity transitions. - * The events can be used to customize or override Activity Transition behavior. - */ - public static class ActivityTransitionListener { - /** - * Called when the enter Transition is ready to start, but hasn't started yet. If - * {@link android.view.Window#getEnterTransition()} is non-null, - * The entering views will be {@link View#INVISIBLE}. - */ - public void onEnterReady() {} - - /** - * Called when the remote exiting transition completes. - */ - public void onRemoteExitComplete() {} - - /** - * Called when the start state for shared elements is captured on enter. - * - * @param sharedElementNames The names of the shared elements that were accepted into - * the View hierarchy. - * @param sharedElements The shared elements that are part of the View hierarchy. - * @param sharedElementSnapshots The Views containing snap shots of the shared element - * from the launching Window. These elements will not - * be part of the scene, but will be positioned relative - * to the Window decor View. - */ - public void onCaptureSharedElementStart(List<String> sharedElementNames, - List<View> sharedElements, List<View> sharedElementSnapshots) {} - - /** - * Called when the end state for shared elements is captured on enter. - * - * @param sharedElementNames The names of the shared elements that were accepted into - * the View hierarchy. - * @param sharedElements The shared elements that are part of the View hierarchy. - * @param sharedElementSnapshots The Views containing snap shots of the shared element - * from the launching Window. These elements will not - * be part of the scene, but will be positioned relative - * to the Window decor View. - */ - public void onCaptureSharedElementEnd(List<String> sharedElementNames, - List<View> sharedElements, List<View> sharedElementSnapshots) {} - - /** - * Called when the enter Transition has been started. - * @param sharedElementNames The names of shared elements that were transferred. - * @param sharedElements The shared elements that were transferred. - */ - public void onStartEnterTransition(List<String> sharedElementNames, - List<View> sharedElements) {} - - /** - * Called when the exit Transition has been started. - * @param sharedElementNames The names of all shared elements that will be transferred. - * @param sharedElements All shared elements that will be transferred. - */ - public void onStartExitTransition(List<String> sharedElementNames, - List<View> sharedElements) {} - - /** - * Called when the exiting shared element transition completes. - */ - public void onSharedElementExitTransitionComplete() {} - - /** - * Called on exit when the shared element has been transferred. - * @param sharedElementNames The names of all shared elements that were transferred. - * @param sharedElements All shared elements that will were transferred. - */ - public void onSharedElementTransferred(List<String> sharedElementNames, - List<View> sharedElements) {} - - /** - * Called when the exit transition has completed. - */ - public void onExitTransitionComplete() {} - - /** - * Returns a mapping from a View in the View hierarchy to the shared element name used - * in the call. This is called twice -- once when the view is - * entering and again when it exits. A null return value indicates that the - * View hierachy can be trusted without any remapping. - * @return A map from a View in the hierarchy to the shared element name used in the - * call. - */ - public Pair<View, String>[] getSharedElementsMapping() { return null; } - - /** - * Returns <code>true</code> if the ActivityTransitionListener will handle removing - * rejected shared elements from the scene. If <code>false</code> is returned, a default - * animation will be used to remove the rejected shared elements from the scene. - * - * @param rejectedSharedElements Views containing visual information of shared elements - * that are not part of the entering scene. These Views - * are positioned relative to the Window decor View. - * @return <code>false</code> if the default animation should be used to remove the - * rejected shared elements from the scene or <code>true</code> if the listener provides - * custom handling. - */ - public boolean handleRejectedSharedElements(List<View> rejectedSharedElements) { - return false; - } - } - - private static class SharedElementMappingListener extends ActivityTransitionListener { - Pair<View, String>[] mSharedElementsMapping = new Pair[1]; - - public SharedElementMappingListener(View view, String name) { - mSharedElementsMapping[0] = Pair.create(view, name); - } - - @Override - public Pair<View, String>[] getSharedElementsMapping() { - return mSharedElementsMapping; - } - } } diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index ca64788..6c6a52f 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -15,27 +15,14 @@ */ package android.app; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Matrix; import android.graphics.Rect; -import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; import android.transition.Transition; -import android.transition.TransitionManager; import android.transition.TransitionSet; import android.util.ArrayMap; -import android.util.Pair; -import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; -import android.view.ViewGroupOverlay; -import android.view.ViewTreeObserver; import android.view.Window; import android.widget.ImageView; @@ -83,14 +70,13 @@ import java.util.Collection; * - onActivityStopped() is called and all exited Views are made VISIBLE. * * Typical finishWithTransition goes like this: - * 1) finishWithTransition() calls startExit() - * - The Window start transitioning to Translucent + * 1) finishWithTransition() creates an ExitTransitionCoordinator and calls startExit() + * - The Window start transitioning to Translucent with a new ActivityOptions. * - If no background exists, a black background is substituted - * - MSG_PREPARE_RESTORE is sent to the ExitTransitionCoordinator * - The shared elements in the scene are matched against those shared elements * that were sent by comparing the names. * - The exit transition is started by setting Views to INVISIBLE. - * 2) MSG_PREPARE_RESTORE is received by the EnterTransitionCoordinator + * 2) The ActivityOptions is received by the Activity and an EnterTransitionCoordinator is created. * - All transitioning views are made VISIBLE to reverse what was done when onActivityStopped() * was called * 3) The Window is made translucent and a callback is received @@ -98,21 +84,21 @@ import java.util.Collection; * 4) The background alpha animation completes * 5) The shared element transition completes * - After both 4 & 5 complete, MSG_TAKE_SHARED_ELEMENTS is sent to the - * ExitTransitionCoordinator - * 6) MSG_TAKE_SHARED_ELEMENTS is received by ExitTransitionCoordinator + * EnterTransitionCoordinator + * 6) MSG_TAKE_SHARED_ELEMENTS is received by EnterTransitionCoordinator * - Shared elements are made VISIBLE * - Shared elements positions and size are set to match the end state of the calling * Activity. * - The shared element transition is started * - If the window allows overlapping transitions, the views transition is started by setting * the entering Views to VISIBLE. - * - MSG_HIDE_SHARED_ELEMENTS is sent to the EnterTransitionCoordinator - * 7) MSG_HIDE_SHARED_ELEMENTS is received by the EnterTransitionCoordinator + * - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator + * 7) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator * - The shared elements are made INVISIBLE * 8) The exit transition completes in the finishing Activity. - * - MSG_EXIT_TRANSITION_COMPLETE is sent to the ExitTransitionCoordinator. + * - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator. * - finish() is called on the exiting Activity - * 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the ExitTransitionCoordinator. + * 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator. * - If the window doesn't allow overlapping enter transitions, the enter transition is started * by setting entering views to VISIBLE. */ @@ -120,30 +106,24 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { private static final String TAG = "ActivityTransitionCoordinator"; /** - * The names of shared elements that are transitioned to the started Activity. - * This is also the name of shared elements that the started Activity accepted. - */ - public static final String KEY_SHARED_ELEMENT_NAMES = "android:shared_element_names"; - - public static final String KEY_SHARED_ELEMENT_STATE = "android:shared_element_state"; - - /** * For Activity transitions, the called Activity's listener to receive calls * when transitions complete. */ - static final String KEY_TRANSITION_RESULTS_RECEIVER = "android:transitionTargetListener"; + static final String KEY_REMOTE_RECEIVER = "android:remoteReceiver"; + + protected static final String KEY_SCREEN_X = "shared_element:screenX"; + protected static final String KEY_SCREEN_Y = "shared_element:screenY"; + protected static final String KEY_TRANSLATION_Z = "shared_element:translationZ"; + protected static final String KEY_WIDTH = "shared_element:width"; + protected static final String KEY_HEIGHT = "shared_element:height"; + protected static final String KEY_BITMAP = "shared_element:bitmap"; + protected static final String KEY_SCALE_TYPE = "shared_element:scaleType"; + protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix"; - private static final String KEY_SCREEN_X = "shared_element:screenX"; - private static final String KEY_SCREEN_Y = "shared_element:screenY"; - private static final String KEY_TRANSLATION_Z = "shared_element:translationZ"; - private static final String KEY_WIDTH = "shared_element:width"; - private static final String KEY_HEIGHT = "shared_element:height"; - private static final String KEY_NAME = "shared_element:name"; - private static final String KEY_BITMAP = "shared_element:bitmap"; - private static final String KEY_SCALE_TYPE = "shared_element:scaleType"; - private static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix"; + // The background fade in/out duration. 150ms is pretty quick, but not abrupt. + public static final int FADE_BACKGROUND_DURATION_MS = 150; - private static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values(); + protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values(); /** * Sent by the exiting coordinator (either EnterTransitionCoordinator @@ -154,7 +134,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { * until this message is received, but may wait for * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true. */ - public static final int MSG_SET_LISTENER = 100; + public static final int MSG_SET_REMOTE_RECEIVER = 100; /** * Sent by the entering coordinator to tell the exiting coordinator @@ -165,17 +145,10 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { public static final int MSG_HIDE_SHARED_ELEMENTS = 101; /** - * Sent by the EnterTransitionCoordinator to tell the - * ExitTransitionCoordinator to hide all of its exited views after - * MSG_ACTIVITY_STOPPED has caused them all to show. - */ - public static final int MSG_PREPARE_RESTORE = 102; - - /** * Sent by the exiting Activity in ActivityOptions#dispatchActivityStopped * to leave the Activity in a good state after it has been hidden. */ - public static final int MSG_ACTIVITY_STOPPED = 103; + public static final int MSG_ACTIVITY_STOPPED = 102; /** * Sent by the exiting coordinator (either EnterTransitionCoordinator @@ -186,7 +159,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { * until this message is received, but may wait for * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true. */ - public static final int MSG_TAKE_SHARED_ELEMENTS = 104; + public static final int MSG_TAKE_SHARED_ELEMENTS = 103; /** * Sent by the exiting coordinator (either @@ -196,309 +169,41 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { * remote coordinator. If it is false, it will trigger the enter * transition to start. */ - public static final int MSG_EXIT_TRANSITION_COMPLETE = 105; + public static final int MSG_EXIT_TRANSITION_COMPLETE = 104; /** * Sent by Activity#startActivity to begin the exit transition. */ - public static final int MSG_START_EXIT_TRANSITION = 106; - - private Window mWindow; - private ArrayList<View> mSharedElements = new ArrayList<View>(); - private ArrayList<String> mTargetSharedNames = new ArrayList<String>(); - private ActivityOptions.ActivityTransitionListener mListener = - new ActivityOptions.ActivityTransitionListener(); - private ArrayList<View> mEnteringViews; - private ResultReceiver mRemoteResultReceiver; - private boolean mNotifiedSharedElementTransitionComplete; - private boolean mNotifiedExitTransitionComplete; - private boolean mSharedElementTransitionStarted; - - private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback(); - - private Transition.TransitionListener mSharedElementListener = - new Transition.TransitionListenerAdapter() { - @Override - public void onTransitionEnd(Transition transition) { - transition.removeListener(this); - onSharedElementTransitionEnd(); - } - }; - - private Transition.TransitionListener mExitListener = - new Transition.TransitionListenerAdapter() { - @Override - public void onTransitionEnd(Transition transition) { - transition.removeListener(this); - onExitTransitionEnd(); - } - }; - - public ActivityTransitionCoordinator(Window window) - { - super(new Handler()); - mWindow = window; - } - - // -------------------- ResultsReceiver Overrides ---------------------- - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - switch (resultCode) { - case MSG_SET_LISTENER: - ResultReceiver resultReceiver - = resultData.getParcelable(KEY_TRANSITION_RESULTS_RECEIVER); - setRemoteResultReceiver(resultReceiver); - onSetResultReceiver(); - break; - case MSG_HIDE_SHARED_ELEMENTS: - onHideSharedElements(); - break; - case MSG_PREPARE_RESTORE: - onPrepareRestore(); - break; - case MSG_EXIT_TRANSITION_COMPLETE: - if (!mSharedElementTransitionStarted) { - send(resultCode, resultData); - } else { - onRemoteSceneExitComplete(); - } - break; - case MSG_TAKE_SHARED_ELEMENTS: - ArrayList<String> sharedElementNames - = resultData.getStringArrayList(KEY_SHARED_ELEMENT_NAMES); - Bundle sharedElementState = resultData.getBundle(KEY_SHARED_ELEMENT_STATE); - onTakeSharedElements(sharedElementNames, sharedElementState); - break; - case MSG_ACTIVITY_STOPPED: - onActivityStopped(); - break; - case MSG_START_EXIT_TRANSITION: - startExit(); - break; - } - } - - // -------------------- calls that can be overridden by subclasses -------------------- - - /** - * Called when MSG_SET_LISTENER is received. This will only be received by - * ExitTransitionCoordinator. - */ - protected void onSetResultReceiver() {} - - /** - * Called when MSG_HIDE_SHARED_ELEMENTS is received - */ - protected void onHideSharedElements() { - setViewVisibility(getSharedElements(), View.INVISIBLE); - mListener.onSharedElementTransferred(getSharedElementNames(), getSharedElements()); - } - - /** - * Called when MSG_PREPARE_RESTORE is called. This will only be received by - * ExitTransitionCoordinator. - */ - protected void onPrepareRestore() { - mListener.onEnterReady(); - } - - /** - * Called when MSG_EXIT_TRANSITION_COMPLETE is received -- the remote coordinator has - * completed its exit transition. This can be called by the ExitTransitionCoordinator when - * starting an Activity or EnterTransitionCoordinator when called with finishWithTransition. - */ - protected void onRemoteSceneExitComplete() { - if (!allowOverlappingTransitions()) { - Transition transition = beginTransition(mEnteringViews, false, true, true); - onStartEnterTransition(transition, mEnteringViews); - } - mListener.onRemoteExitComplete(); - } - - /** - * Called when MSG_TAKE_SHARED_ELEMENTS is received. This means that the shared elements are - * in a stable state and ready to move to the Window. - * @param sharedElementNames The names of the shared elements to move. - * @param state Contains the shared element states (size & position) - */ - protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) { - setSharedElements(); - reconcileSharedElements(sharedElementNames); - mEnteringViews.removeAll(mSharedElements); - final ArrayList<View> accepted = new ArrayList<View>(); - final ArrayList<View> rejected = new ArrayList<View>(); - createSharedElementImages(accepted, rejected, sharedElementNames, state); - ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState = - setSharedElementState(state, accepted); - handleRejected(rejected); - - if (getViewsTransition() != null) { - setViewVisibility(mEnteringViews, View.INVISIBLE); - } - setViewVisibility(mSharedElements, View.VISIBLE); - Transition transition = beginTransition(mEnteringViews, true, allowOverlappingTransitions(), - true); - setOriginalImageViewState(originalImageViewState); - - if (allowOverlappingTransitions()) { - onStartEnterTransition(transition, mEnteringViews); - } - - mRemoteResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null); - } + public static final int MSG_START_EXIT_TRANSITION = 105; /** - * Called when MSG_ACTIVITY_STOPPED is received. This is received when Activity.onStop is - * called after running startActivity* is called using an Activity Transition. + * It took too long for a message from the entering Activity, so we canceled the transition. */ - protected void onActivityStopped() {} - - /** - * Called when the start transition is ready to run. This may be immediately after - * MSG_TAKE_SHARED_ELEMENTS or MSG_EXIT_TRANSITION_COMPLETE, depending on whether - * overlapping transitions are allowed. - * @param transition The transition currently started. - * @param enteringViews The views entering the scene. This won't include shared elements. - */ - protected void onStartEnterTransition(Transition transition, ArrayList<View> enteringViews) { + public static final int MSG_CANCEL = 106; + + final private Window mWindow; + final protected ArrayList<String> mAllSharedElementNames; + final protected ArrayList<View> mSharedElements = new ArrayList<View>(); + final protected ArrayList<String> mSharedElementNames = new ArrayList<String>(); + final protected ArrayList<View> mTransitioningViews = new ArrayList<View>(); + final protected SharedElementListener mListener; + protected ResultReceiver mResultReceiver; + final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback(); + + public ActivityTransitionCoordinator(Window window, + ArrayList<String> allSharedElementNames, + ArrayList<String> accepted, ArrayList<String> localNames, + SharedElementListener listener) { + super(new Handler()); + mWindow = window; + mListener = listener; + mAllSharedElementNames = allSharedElementNames; + setSharedElements(accepted, localNames); if (getViewsTransition() != null) { - setViewVisibility(enteringViews, View.VISIBLE); + getDecor().captureTransitioningViews(mTransitioningViews); + mTransitioningViews.removeAll(mSharedElements); } - mEnteringViews = null; - mListener.onStartEnterTransition(getSharedElementNames(), getSharedElements()); - } - - /** - * Called when the exit transition has started. - * @param exitingViews The views leaving the scene. This won't include shared elements. - */ - protected void onStartExitTransition(ArrayList<View> exitingViews) {} - - /** - * Called during the exit when the shared element transition has completed. - */ - protected void onSharedElementTransitionEnd() { - Bundle bundle = new Bundle(); - int[] tempLoc = new int[2]; - for (int i = 0; i < mSharedElements.size(); i++) { - View sharedElement = mSharedElements.get(i); - String name = mTargetSharedNames.get(i); - captureSharedElementState(sharedElement, name, bundle, tempLoc); - } - Bundle allValues = new Bundle(); - allValues.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, getSharedElementNames()); - allValues.putBundle(KEY_SHARED_ELEMENT_STATE, bundle); - sharedElementTransitionComplete(allValues); - mListener.onSharedElementExitTransitionComplete(); - } - - /** - * Called after the shared element transition is complete to pass the shared element state - * to the remote coordinator. - * @param bundle The Bundle to send to the coordinator containing the shared element state. - */ - protected abstract void sharedElementTransitionComplete(Bundle bundle); - - /** - * Called when the exit transition finishes. - */ - protected void onExitTransitionEnd() { - mListener.onExitTransitionComplete(); - } - - /** - * Called to start the exit transition. Launched from ActivityOptions#dispatchStartExit - */ - protected abstract void startExit(); - - /** - * A non-null transition indicates that the Views of the Window should be made INVISIBLE. - * @return The Transition used to cause transitioning views to either enter or exit the scene. - */ - protected abstract Transition getViewsTransition(); - - /** - * @return The Transition used to move the shared elements from the start position and size - * to the end position and size. - */ - protected abstract Transition getSharedElementTransition(); - - /** - * @return When the enter transition should overlap with the exit transition of the - * remote controller. - */ - protected abstract boolean allowOverlappingTransitions(); - - // called by subclasses - - protected void notifySharedElementTransitionComplete(Bundle sharedElements) { - if (!mNotifiedSharedElementTransitionComplete) { - mNotifiedSharedElementTransitionComplete = true; - mRemoteResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, sharedElements); - } - } - - protected void notifyExitTransitionComplete() { - if (!mNotifiedExitTransitionComplete) { - mNotifiedExitTransitionComplete = true; - mRemoteResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null); - } - } - - protected void notifyPrepareRestore() { - mRemoteResultReceiver.send(MSG_PREPARE_RESTORE, null); - } - - protected void setRemoteResultReceiver(ResultReceiver resultReceiver) { - mRemoteResultReceiver = resultReceiver; - } - - protected void notifySetListener() { - Bundle bundle = new Bundle(); - bundle.putParcelable(KEY_TRANSITION_RESULTS_RECEIVER, this); - mRemoteResultReceiver.send(MSG_SET_LISTENER, bundle); - } - - protected void setEnteringViews(ArrayList<View> views) { - mEnteringViews = views; - } - - protected void setSharedElements() { - Pair<View, String>[] sharedElements = mListener.getSharedElementsMapping(); - mSharedElements.clear(); - mTargetSharedNames.clear(); - if (sharedElements == null) { - ArrayMap<String, View> map = new ArrayMap<String, View>(); - if (getViewsTransition() != null) { - setViewVisibility(mEnteringViews, View.VISIBLE); - } - getDecor().findNamedViews(map); - if (getViewsTransition() != null) { - setViewVisibility(mEnteringViews, View.INVISIBLE); - } - for (int i = 0; i < map.size(); i++) { - View view = map.valueAt(i); - String name = map.keyAt(i); - mSharedElements.add(view); - mTargetSharedNames.add(name); - } - } else { - for (int i = 0; i < sharedElements.length; i++) { - Pair<View, String> viewStringPair = sharedElements[i]; - View view = viewStringPair.first; - String name = viewStringPair.second; - mSharedElements.add(view); - mTargetSharedNames.add(name); - } - } - } - - protected ArrayList<View> getSharedElements() { - return mSharedElements; - } - - protected ArrayList<String> getSharedElementNames() { - return mTargetSharedNames; + setEpicenter(); } protected Window getWindow() { @@ -509,238 +214,43 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return (mWindow == null) ? null : (ViewGroup) mWindow.getDecorView(); } - protected void startExitTransition(ArrayList<String> sharedElements) { - setSharedElements(); - reconcileSharedElements(sharedElements); - ArrayList<View> transitioningViews = captureTransitioningViews(); - beginTransition(transitioningViews, true, true, false); - onStartExitTransition(transitioningViews); - if (getViewsTransition() != null) { - setViewVisibility(transitioningViews, View.INVISIBLE); + /** + * Sets the transition epicenter to the position of the first shared element. + */ + protected void setEpicenter() { + View epicenter = null; + if (!mAllSharedElementNames.isEmpty() && !mSharedElementNames.isEmpty() && + mAllSharedElementNames.get(0).equals(mSharedElementNames.get(0))) { + epicenter = mSharedElements.get(0); } - mListener.onStartExitTransition(getSharedElementNames(), getSharedElements()); - } - - protected void clearConnections() { - mRemoteResultReceiver = null; + setEpicenter(epicenter); } - // public API - - public void setActivityTransitionListener(ActivityOptions.ActivityTransitionListener listener) { - if (listener == null) { - mListener = new ActivityOptions.ActivityTransitionListener(); + private void setEpicenter(View view) { + if (view == null) { + mEpicenterCallback.setEpicenter(null); } else { - mListener = listener; + int[] loc = new int[2]; + view.getLocationOnScreen(loc); + int left = loc[0] + Math.round(view.getTranslationX()); + int top = loc[1] + Math.round(view.getTranslationY()); + int right = left + view.getWidth(); + int bottom = top + view.getHeight(); + Rect epicenter = new Rect(left, top, right, bottom); + mEpicenterCallback.setEpicenter(epicenter); } } - // private methods - - private Transition configureTransition(Transition transition) { - if (transition != null) { - transition = transition.clone(); - transition.setEpicenterCallback(mEpicenterCallback); - } - return transition; - } - - private void reconcileSharedElements(ArrayList<String> sharedElementNames) { - // keep only those that are in sharedElementNames. - int numSharedElements = sharedElementNames.size(); - int targetIndex = 0; - for (int i = 0; i < numSharedElements; i++) { - String name = sharedElementNames.get(i); - int index = mTargetSharedNames.indexOf(name); - if (index >= 0) { - // Swap the items at the indexes if necessary. - if (index != targetIndex) { - View temp = mSharedElements.get(index); - mSharedElements.set(index, mSharedElements.get(targetIndex)); - mSharedElements.set(targetIndex, temp); - mTargetSharedNames.set(index, mTargetSharedNames.get(targetIndex)); - mTargetSharedNames.set(targetIndex, name); - } - targetIndex++; - } - } - for (int i = mSharedElements.size() - 1; i >= targetIndex; i--) { - mSharedElements.remove(i); - mTargetSharedNames.remove(i); - } - Rect epicenter = null; - if (!mTargetSharedNames.isEmpty() - && mTargetSharedNames.get(0).equals(sharedElementNames.get(0))) { - epicenter = calcEpicenter(mSharedElements.get(0)); - } - mEpicenterCallback.setEpicenter(epicenter); - } - - private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState( - Bundle sharedElementState, final ArrayList<View> acceptedOverlayViews) { - ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState = - new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>(); - final int[] tempLoc = new int[2]; - if (sharedElementState != null) { - for (int i = 0; i < mSharedElements.size(); i++) { - View sharedElement = mSharedElements.get(i); - String name = mTargetSharedNames.get(i); - Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement, - name, sharedElementState); - if (originalState != null) { - originalImageState.put((ImageView) sharedElement, originalState); - } - View parent = (View) sharedElement.getParent(); - parent.getLocationOnScreen(tempLoc); - setSharedElementState(sharedElement, name, sharedElementState, tempLoc); - sharedElement.requestLayout(); - } - } - mListener.onCaptureSharedElementStart(mTargetSharedNames, mSharedElements, - acceptedOverlayViews); - - getDecor().getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - getDecor().getViewTreeObserver().removeOnPreDrawListener(this); - mListener.onCaptureSharedElementEnd(mTargetSharedNames, mSharedElements, - acceptedOverlayViews); - mSharedElementTransitionStarted = true; - return true; - } - } - ); - return originalImageState; - } - - private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name, - Bundle transitionArgs) { - if (!(view instanceof ImageView)) { - return null; - } - Bundle bundle = transitionArgs.getBundle(name); - int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1); - if (scaleTypeInt < 0) { - return null; - } - - ImageView imageView = (ImageView) view; - ImageView.ScaleType originalScaleType = imageView.getScaleType(); - - Matrix originalMatrix = null; - if (originalScaleType == ImageView.ScaleType.MATRIX) { - originalMatrix = new Matrix(imageView.getImageMatrix()); - } - - return Pair.create(originalScaleType, originalMatrix); - } - - /** - * Sets the captured values from a previous - * {@link #captureSharedElementState(android.view.View, String, android.os.Bundle, int[])} - * @param view The View to apply placement changes to. - * @param name The shared element name given from the source Activity. - * @param transitionArgs A <code>Bundle</code> containing all placementinformation for named - * shared elements in the scene. - * @param parentLoc The x and y coordinates of the parent's screen position. - */ - private static void setSharedElementState(View view, String name, Bundle transitionArgs, - int[] parentLoc) { - Bundle sharedElementBundle = transitionArgs.getBundle(name); - if (sharedElementBundle == null) { - return; - } - - if (view instanceof ImageView) { - int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1); - if (scaleTypeInt >= 0) { - ImageView imageView = (ImageView) view; - ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt]; - imageView.setScaleType(scaleType); - if (scaleType == ImageView.ScaleType.MATRIX) { - float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX); - Matrix matrix = new Matrix(); - matrix.setValues(matrixValues); - imageView.setImageMatrix(matrix); - } - } - } - - float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z); - view.setTranslationZ(z); - - int x = sharedElementBundle.getInt(KEY_SCREEN_X); - int y = sharedElementBundle.getInt(KEY_SCREEN_Y); - int width = sharedElementBundle.getInt(KEY_WIDTH); - int height = sharedElementBundle.getInt(KEY_HEIGHT); - - int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); - int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); - view.measure(widthSpec, heightSpec); - - int left = x - parentLoc[0]; - int top = y - parentLoc[1]; - int right = left + width; - int bottom = top + height; - view.layout(left, top, right, bottom); + public ArrayList<String> getAcceptedNames() { + return mSharedElementNames; } - /** - * Captures placement information for Views with a shared element name for - * Activity Transitions. - * @param view The View to capture the placement information for. - * @param name The shared element name in the target Activity to apply the placement - * information for. - * @param transitionArgs Bundle to store shared element placement information. - * @param tempLoc A temporary int[2] for capturing the current location of views. - * @see #setSharedElementState(android.view.View, String, android.os.Bundle, int[]) - */ - private static void captureSharedElementState(View view, String name, Bundle transitionArgs, - int[] tempLoc) { - Bundle sharedElementBundle = new Bundle(); - view.getLocationOnScreen(tempLoc); - float scaleX = view.getScaleX(); - sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]); - int width = Math.round(view.getWidth() * scaleX); - sharedElementBundle.putInt(KEY_WIDTH, width); - - float scaleY = view.getScaleY(); - sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]); - int height= Math.round(view.getHeight() * scaleY); - sharedElementBundle.putInt(KEY_HEIGHT, height); - - sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ()); - - sharedElementBundle.putString(KEY_NAME, view.getViewName()); - - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - view.draw(canvas); - sharedElementBundle.putParcelable(KEY_BITMAP, bitmap); - - if (view instanceof ImageView) { - ImageView imageView = (ImageView) view; - int scaleTypeInt = scaleTypeToInt(imageView.getScaleType()); - sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt); - if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) { - float[] matrix = new float[9]; - imageView.getImageMatrix().getValues(matrix); - sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix); - } + public ArrayList<String> getMappedNames() { + ArrayList<String> names = new ArrayList<String>(mSharedElements.size()); + for (int i = 0; i < mSharedElements.size(); i++) { + names.add(mSharedElements.get(i).getViewName()); } - - transitionArgs.putBundle(name, sharedElementBundle); - } - - private static Rect calcEpicenter(View view) { - int[] loc = new int[2]; - view.getLocationOnScreen(loc); - int left = loc[0] + Math.round(view.getTranslationX()); - int top = loc[1] + Math.round(view.getTranslationY()); - int right = left + view.getWidth(); - int bottom = top + view.getHeight(); - return new Rect(left, top, right, bottom); + return names; } public static void setViewVisibility(Collection<View> views, int visibility) { @@ -751,12 +261,12 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { } } - private static Transition addTransitionTargets(Transition transition, Collection<View> views) { + protected static Transition addTargets(Transition transition, Collection<View> views) { if (transition == null || views == null || views.isEmpty()) { return null; } TransitionSet set = new TransitionSet(); - set.addTransition(transition.clone()); + set.addTransition(transition); if (views != null) { for (View view: views) { set.addTarget(view); @@ -765,152 +275,62 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return set; } - private ArrayList<View> captureTransitioningViews() { - if (getViewsTransition() == null) { - return null; - } - ArrayList<View> transitioningViews = new ArrayList<View>(); - getDecor().captureTransitioningViews(transitioningViews); - transitioningViews.removeAll(getSharedElements()); - return transitioningViews; - } - - private Transition getSharedElementTransition(boolean isEnter) { - Transition transition = getSharedElementTransition(); - if (transition == null) { - return null; - } - transition = configureTransition(transition); - if (!isEnter) { - transition.addListener(mSharedElementListener); - } - return transition; - } - - private Transition getViewsTransition(ArrayList<View> transitioningViews, boolean isEnter) { - Transition transition = getViewsTransition(); - if (transition == null) { - return null; - } - transition = configureTransition(transition); - if (!isEnter) { - transition.addListener(mExitListener); - } - return addTransitionTargets(transition, transitioningViews); - } - - private Transition beginTransition(ArrayList<View> transitioningViews, - boolean transitionSharedElement, boolean transitionViews, boolean isEnter) { - Transition sharedElementTransition = null; - if (transitionSharedElement) { - sharedElementTransition = getSharedElementTransition(isEnter); - if (!isEnter && sharedElementTransition == null) { - onSharedElementTransitionEnd(); - } - } - Transition viewsTransition = null; - if (transitionViews) { - viewsTransition = getViewsTransition(transitioningViews, isEnter); - if (!isEnter && viewsTransition == null) { - onExitTransitionEnd(); - } - } - - Transition transition = null; - if (sharedElementTransition == null) { - transition = viewsTransition; - } else if (viewsTransition == null) { - transition = sharedElementTransition; - } else { - TransitionSet set = new TransitionSet(); - set.addTransition(sharedElementTransition); - set.addTransition(viewsTransition); - transition = set; - } + protected Transition configureTransition(Transition transition) { if (transition != null) { - TransitionManager.beginDelayedTransition(getDecor(), transition); - if (transitionSharedElement && !mSharedElements.isEmpty()) { - mSharedElements.get(0).invalidate(); - } else if (transitionViews && !transitioningViews.isEmpty()) { - transitioningViews.get(0).invalidate(); - } + transition = transition.clone(); + transition.setEpicenterCallback(mEpicenterCallback); } return transition; } - private void handleRejected(final ArrayList<View> rejected) { - int numRejected = rejected.size(); - if (numRejected == 0) { - return; - } - boolean rejectionHandled = mListener.handleRejectedSharedElements(rejected); - if (rejectionHandled) { - return; - } - - ViewGroupOverlay overlay = getDecor().getOverlay(); - ObjectAnimator animator = null; - for (int i = 0; i < numRejected; i++) { - View view = rejected.get(i); - overlay.add(view); - animator = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0); - animator.start(); - } - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - ViewGroupOverlay overlay = getDecor().getOverlay(); - for (int i = rejected.size() - 1; i >= 0; i--) { - overlay.remove(rejected.get(i)); + protected static Transition mergeTransitions(Transition transition1, Transition transition2) { + if (transition1 == null) { + return transition2; + } else if (transition2 == null) { + return transition1; + } else { + TransitionSet transitionSet = new TransitionSet(); + transitionSet.addTransition(transition1); + transitionSet.addTransition(transition2); + return transitionSet; + } + } + + private void setSharedElements(ArrayList<String> accepted, ArrayList<String> localNames) { + if (!mAllSharedElementNames.isEmpty()) { + ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); + getDecor().findNamedViews(sharedElements); + if (accepted != null) { + for (int i = 0; i < localNames.size(); i++) { + String localName = localNames.get(i); + String acceptedName = accepted.get(i); + if (!localName.equals(acceptedName)) { + View view = sharedElements.remove(localName); + if (view != null) { + sharedElements.put(acceptedName, view); + } + } } } - }); - } - - private void createSharedElementImages(ArrayList<View> accepted, ArrayList<View> rejected, - ArrayList<String> sharedElementNames, Bundle state) { - int numSharedElements = sharedElementNames.size(); - Context context = getWindow().getContext(); - int[] parentLoc = new int[2]; - getDecor().getLocationOnScreen(parentLoc); - for (int i = 0; i < numSharedElements; i++) { - String name = sharedElementNames.get(i); - Bundle sharedElementBundle = state.getBundle(name); - if (sharedElementBundle != null) { - Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP); - ImageView imageView = new ImageView(context); - imageView.setId(com.android.internal.R.id.shared_element); - imageView.setScaleType(ImageView.ScaleType.CENTER); - imageView.setImageBitmap(bitmap); - imageView.setViewName(name); - setSharedElementState(imageView, name, state, parentLoc); - if (mTargetSharedNames.contains(name)) { - accepted.add(imageView); - } else { - rejected.add(imageView); + sharedElements.retainAll(mAllSharedElementNames); + mListener.remapSharedElements(mAllSharedElementNames, sharedElements); + sharedElements.retainAll(mAllSharedElementNames); + for (int i = 0; i < mAllSharedElementNames.size(); i++) { + String name = mAllSharedElementNames.get(i); + View sharedElement = sharedElements.get(name); + if (sharedElement != null) { + mSharedElementNames.add(name); + mSharedElements.add(sharedElement); } } } } - private static void setOriginalImageViewState( - ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) { - for (int i = 0; i < originalState.size(); i++) { - ImageView imageView = originalState.keyAt(i); - Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i); - imageView.setScaleType(state.first); - imageView.setImageMatrix(state.second); - } + protected void setResultReceiver(ResultReceiver resultReceiver) { + mResultReceiver = resultReceiver; } - private static int scaleTypeToInt(ImageView.ScaleType scaleType) { - for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) { - if (scaleType == SCALE_TYPE_VALUES[i]) { - return i; - } - } - return -1; - } + protected abstract Transition getViewsTransition(); private static class FixedEpicenterCallback extends Transition.EpicenterCallback { private Rect mEpicenter; diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java new file mode 100644 index 0000000..63019b6 --- /dev/null +++ b/core/java/android/app/ActivityTransitionState.java @@ -0,0 +1,204 @@ +/* + * 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; + +import android.os.Bundle; +import android.os.ResultReceiver; +import android.util.ArrayMap; +import android.view.View; +import android.view.Window; + +import java.util.ArrayList; + +/** + * This class contains all persistence-related functionality for Activity Transitions. + * Activities start exit and enter Activity Transitions through this class. + */ +class ActivityTransitionState { + + private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements"; + + private static final String ENTERING_MAPPED_FROM = "android:enteringMappedFrom"; + + private static final String ENTERING_MAPPED_TO = "android:enteringMappedTo"; + + private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom"; + + private static final String EXITING_MAPPED_TO = "android:exitingMappedTo"; + + /** + * The shared elements that the calling Activity has said that they transferred to this + * Activity. + */ + private ArrayList<String> mEnteringNames; + + /** + * The shared elements that this Activity as accepted and mapped to local Views. + */ + private ArrayList<String> mEnteringFrom; + + /** + * The names of local Views that are mapped to those elements in mEnteringFrom. + */ + private ArrayList<String> mEnteringTo; + + /** + * The names of shared elements that were shared to the called Activity. + */ + private ArrayList<String> mExitingFrom; + + /** + * The names of local Views that were shared out, mapped to those elements in mExitingFrom. + */ + private ArrayList<String> mExitingTo; + + /** + * The ActivityOptions used to call an Activity. Used to make the elements restore + * Visibility of exited Views. + */ + private ActivityOptions mCalledActivityOptions; + + /** + * We must be able to cancel entering transitions to stop changing the Window to + * opaque when we exit before making the Window opaque. + */ + private EnterTransitionCoordinator mEnterTransitionCoordinator; + + /** + * ActivityOptions used on entering this Activity. + */ + private ActivityOptions mEnterActivityOptions; + + /** + * Has an exit transition been started? If so, we don't want to double-exit. + */ + private boolean mHasExited; + + public ActivityTransitionState() { + } + + public void readState(Bundle bundle) { + if (bundle != null) { + if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) { + mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS); + mEnteringFrom = bundle.getStringArrayList(ENTERING_MAPPED_FROM); + mEnteringTo = bundle.getStringArrayList(ENTERING_MAPPED_TO); + } + if (mEnterTransitionCoordinator == null) { + mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM); + mExitingTo = bundle.getStringArrayList(EXITING_MAPPED_TO); + } + } + } + + public void saveState(Bundle bundle) { + if (mEnteringNames != null) { + bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames); + bundle.putStringArrayList(ENTERING_MAPPED_FROM, mEnteringFrom); + bundle.putStringArrayList(ENTERING_MAPPED_TO, mEnteringTo); + } + if (mExitingFrom != null) { + bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom); + bundle.putStringArrayList(EXITING_MAPPED_TO, mExitingTo); + } + } + + public void setEnterActivityOptions(Activity activity, ActivityOptions options) { + if (activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS) + && options != null && mEnterActivityOptions == null + && options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { + mEnterActivityOptions = options; + if (mEnterActivityOptions.isReturning()) { + int result = mEnterActivityOptions.getResultCode(); + if (result != 0) { + activity.onActivityReenter(result, mEnterActivityOptions.getResultData()); + } + } + } + } + + public void enterReady(Activity activity) { + if (mEnterActivityOptions == null) { + return; + } + mHasExited = false; + ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames(); + ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver(); + if (mEnterActivityOptions.isReturning()) { + if (mCalledActivityOptions != null) { + mCalledActivityOptions.dispatchActivityStopped(); + mCalledActivityOptions = null; + } + activity.getWindow().getDecorView().setVisibility(View.VISIBLE); + mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, + resultReceiver, sharedElementNames, mExitingFrom, mExitingTo); + } else { + mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity, + resultReceiver, sharedElementNames, null, null); + mEnteringNames = sharedElementNames; + mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames(); + mEnteringTo = mEnterTransitionCoordinator.getMappedNames(); + } + mExitingFrom = null; + mExitingTo = null; + mEnterActivityOptions = null; + } + + public void onStop() { + if (mCalledActivityOptions != null) { + mCalledActivityOptions.dispatchActivityStopped(); + mCalledActivityOptions = null; + } + if (mEnterTransitionCoordinator != null) { + mEnterTransitionCoordinator.stop(); + mEnterTransitionCoordinator = null; + } + } + + public boolean startExitBackTransition(Activity activity) { + if (mEnteringNames == null) { + return false; + } else { + if (!mHasExited) { + mHasExited = true; + if (mEnterTransitionCoordinator != null) { + mEnterTransitionCoordinator.stop(); + mEnterTransitionCoordinator = null; + } + ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); + activity.getWindow().getDecorView().findNamedViews(sharedElements); + + ExitTransitionCoordinator exitCoordinator = + new ExitTransitionCoordinator(activity, mEnteringNames, mEnteringFrom, + mEnteringTo, true); + exitCoordinator.startExit(activity.mResultCode, activity.mResultData); + } + return true; + } + } + + public void startExitOutTransition(Activity activity, Bundle options) { + if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) { + return; + } + mCalledActivityOptions = new ActivityOptions(options); + if (mCalledActivityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { + mExitingFrom = mCalledActivityOptions.getSharedElementNames(); + mExitingTo = mCalledActivityOptions.getLocalSharedElementNames(); + mCalledActivityOptions.dispatchStartExit(); + } + } +} diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index d813dab..62c4f0f 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -19,6 +19,7 @@ package android.app; import android.Manifest; import android.os.Binder; import android.os.IBinder; +import android.os.UserManager; import android.util.ArrayMap; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsCallback; @@ -412,6 +413,58 @@ public class AppOpsManager { }; /** + * Specifies whether an Op should be restricted by a user restriction. + * Each Op should be filled with a restriction string from UserManager or + * null to specify it is not affected by any user restriction. + */ + private static String[] sOpRestrictions = new String[] { + null, //COARSE_LOCATION + null, //FINE_LOCATION + null, //GPS + null, //VIBRATE + null, //READ_CONTACTS + null, //WRITE_CONTACTS + null, //READ_CALL_LOG + null, //WRITE_CALL_LOG + null, //READ_CALENDAR + null, //WRITE_CALENDAR + null, //WIFI_SCAN + null, //POST_NOTIFICATION + null, //NEIGHBORING_CELLS + null, //CALL_PHONE + null, //READ_SMS + null, //WRITE_SMS + null, //RECEIVE_SMS + null, //RECEIVE_EMERGECY_SMS + null, //RECEIVE_MMS + null, //RECEIVE_WAP_PUSH + null, //SEND_SMS + null, //READ_ICC_SMS + null, //WRITE_ICC_SMS + null, //WRITE_SETTINGS + null, //SYSTEM_ALERT_WINDOW + null, //ACCESS_NOTIFICATIONS + null, //CAMERA + null, //RECORD_AUDIO + null, //PLAY_AUDIO + null, //READ_CLIPBOARD + null, //WRITE_CLIPBOARD + null, //TAKE_MEDIA_BUTTONS + null, //TAKE_AUDIO_FOCUS + null, //AUDIO_MASTER_VOLUME + null, //AUDIO_VOICE_VOLUME + null, //AUDIO_RING_VOLUME + null, //AUDIO_MEDIA_VOLUME + null, //AUDIO_ALARM_VOLUME + null, //AUDIO_NOTIFICATION_VOLUME + null, //AUDIO_BLUETOOTH_VOLUME + null, //WAKE_LOCK + null, //MONITOR_LOCATION + null, //MONITOR_HIGH_POWER_LOCATION + null, //GET_USAGE_STATS + }; + + /** * This specifies the default mode for each operation. */ private static int[] sOpDefaultMode = new int[] { @@ -542,6 +595,10 @@ public class AppOpsManager { throw new IllegalStateException("sOpDisableReset length " + sOpDisableReset.length + " should be " + _NUM_OP); } + if (sOpRestrictions.length != _NUM_OP) { + throw new IllegalStateException("sOpRestrictions length " + sOpRestrictions.length + + " should be " + _NUM_OP); + } for (int i=0; i<_NUM_OP; i++) { if (sOpToString[i] != null) { sOpStrToOp.put(sOpToString[i], i); @@ -575,6 +632,14 @@ public class AppOpsManager { } /** + * Retrieve the user restriction associated with an operation, or null if there is not one. + * @hide + */ + public static String opToRestriction(int op) { + return sOpRestrictions[op]; + } + + /** * Retrieve the default mode for the operation. * @hide */ diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index efd3d86..2f35160 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -35,6 +35,7 @@ import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; @@ -1127,7 +1128,7 @@ final class ApplicationPackageManager extends PackageManager { public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName) { try { - mPM.installPackageEtc(packageURI, null, observer.mObserver, + mPM.installPackageEtc(packageURI, null, observer.getBinder(), flags, installerPackageName); } catch (RemoteException e) { // Should never happen! @@ -1140,7 +1141,7 @@ final class ApplicationPackageManager extends PackageManager { Uri verificationURI, ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { try { - mPM.installPackageWithVerificationEtc(packageURI, null, observer.mObserver, flags, + mPM.installPackageWithVerificationEtc(packageURI, null, observer.getBinder(), flags, installerPackageName, verificationURI, manifestDigest, encryptionParams); } catch (RemoteException e) { // Should never happen! @@ -1153,7 +1154,7 @@ final class ApplicationPackageManager extends PackageManager { VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { try { mPM.installPackageWithVerificationAndEncryptionEtc(packageURI, null, - observer.mObserver, flags, installerPackageName, verificationParams, + observer.getBinder(), flags, installerPackageName, verificationParams, encryptionParams); } catch (RemoteException e) { // Should never happen! @@ -1440,6 +1441,16 @@ final class ApplicationPackageManager extends PackageManager { return null; } + @Override + public PackageInstaller getPackageInstaller() { + try { + return new PackageInstaller(this, mPM.getPackageInstaller(), mContext.getUserId(), + mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + /** * @hide */ diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index d2d8ed1..636205b 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -18,270 +18,414 @@ package android.app; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; -import android.graphics.drawable.ColorDrawable; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Matrix; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; import android.os.ResultReceiver; import android.transition.Transition; +import android.transition.TransitionManager; +import android.util.ArrayMap; +import android.util.Pair; import android.view.View; +import android.view.ViewGroupOverlay; import android.view.ViewTreeObserver; -import android.view.Window; +import android.widget.ImageView; import java.util.ArrayList; +import java.util.Collection; /** * This ActivityTransitionCoordinator is created by the Activity to manage - * the enter scene and shared element transfer as well as Activity#finishWithTransition - * exiting the Scene and transferring shared elements back to the called Activity. + * the enter scene and shared element transfer into the Scene, either during + * launch of an Activity or returning from a launched Activity. */ -class EnterTransitionCoordinator extends ActivityTransitionCoordinator - implements ViewTreeObserver.OnPreDrawListener { +class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private static final String TAG = "EnterTransitionCoordinator"; - // The background fade in/out duration. 150ms is pretty quick, but not abrupt. - private static final int FADE_BACKGROUND_DURATION_MS = 150; + private static final long MAX_WAIT_MS = 1500; - /** - * The shared element names sent by the ExitTransitionCoordinator and may be - * shared when exiting back. - */ - private ArrayList<String> mEnteringSharedElementNames; - - /** - * The Activity that has created this coordinator. This is used solely to make the - * Window translucent/opaque. - */ + private boolean mSharedElementTransitionStarted; + private boolean mIsReturning; private Activity mActivity; - - /** - * True if the Window was opaque at the start and we should make it opaque again after - * enter transitions have completed. - */ - private boolean mWasOpaque; - - /** - * During exit, is the background alpha == 0? - */ - private boolean mBackgroundFadedOut; - - /** - * During exit, has the shared element transition completed? - */ - private boolean mSharedElementTransitionComplete; - - /** - * Has the exit started? We don't want to accidentally exit multiple times. e.g. when - * back is hit twice during the exit animation. - */ - private boolean mExitTransitionStarted; - - /** - * Has the exit transition ended? - */ - private boolean mExitTransitionComplete; - - /** - * We only want to make the Window transparent and set the background alpha once. After that, - * the Activity won't want the same enter transition. - */ - private boolean mMadeReady; - - /** - * True if Window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS) -- this means that - * enter and exit transitions should be active. - */ - private boolean mSupportsTransition; - - /** - * Background alpha animations may complete prior to receiving the callback for - * onTranslucentConversionComplete. If so, we need to immediately call to make the Window - * opaque. - */ - private boolean mMakeOpaque; - - public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver) { - super(activity.getWindow()); + private boolean mHasStopped; + private Handler mHandler; + private boolean mIsCanceled; + + public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, + ArrayList<String> sharedElementNames, + ArrayList<String> acceptedNames, ArrayList<String> mappedNames) { + super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames, + activity.mTransitionListener); mActivity = activity; - setRemoteResultReceiver(resultReceiver); + mIsReturning = acceptedNames != null; + setResultReceiver(resultReceiver); + prepareEnter(); + Bundle resultReceiverBundle = new Bundle(); + resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this); + mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle); + if (mIsReturning) { + mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + cancel(); + } + }; + mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS); + } } - public void readyToEnter() { - if (!mMadeReady) { - mMadeReady = true; - mSupportsTransition = getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS); - if (mSupportsTransition) { - Window window = getWindow(); - window.getDecorView().getViewTreeObserver().addOnPreDrawListener(this); - mActivity.overridePendingTransition(0, 0); - mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() { - @Override - public void onTranslucentConversionComplete(boolean drawComplete) { - mWasOpaque = true; - if (mMakeOpaque) { - mActivity.convertFromTranslucent(); - } + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + switch (resultCode) { + case MSG_TAKE_SHARED_ELEMENTS: + if (!mIsCanceled) { + if (mHandler != null) { + mHandler.removeMessages(MSG_CANCEL); } - }, null); - Drawable background = getDecor().getBackground(); - if (background != null) { - window.setBackgroundDrawable(null); - background.setAlpha(0); - window.setBackgroundDrawable(background); + onTakeSharedElements(resultData); } + break; + case MSG_EXIT_TRANSITION_COMPLETE: + if (!mIsCanceled) { + if (!mSharedElementTransitionStarted) { + send(resultCode, resultData); + } else { + onRemoteExitTransitionComplete(); + } + } + break; + case MSG_CANCEL: + cancel(); + break; + } + } + + private void cancel() { + if (!mIsCanceled) { + mIsCanceled = true; + if (getViewsTransition() == null) { + setViewVisibility(mSharedElements, View.VISIBLE); + } else { + mTransitioningViews.addAll(mSharedElements); } + mSharedElementNames.clear(); + mSharedElements.clear(); + mAllSharedElementNames.clear(); + onTakeSharedElements(null); + onRemoteExitTransitionComplete(); } } - @Override - protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) { - mEnteringSharedElementNames = new ArrayList<String>(); - mEnteringSharedElementNames.addAll(sharedElementNames); - super.onTakeSharedElements(sharedElementNames, state); + public boolean isReturning() { + return mIsReturning; } - @Override - protected void sharedElementTransitionComplete(Bundle bundle) { - notifySharedElementTransitionComplete(bundle); - exitAfterSharedElementTransition(); + protected void prepareEnter() { + setViewVisibility(mSharedElements, View.INVISIBLE); + if (getViewsTransition() != null) { + setViewVisibility(mTransitioningViews, View.INVISIBLE); + } + mActivity.overridePendingTransition(0, 0); + if (!mIsReturning) { + mActivity.convertToTranslucent(null, null); + Drawable background = getDecor().getBackground(); + if (background != null) { + getWindow().setBackgroundDrawable(null); + background = background.mutate(); + background.setAlpha(0); + getWindow().setBackgroundDrawable(background); + } + } else { + mActivity = null; // all done with it now. + } } - @Override - public boolean onPreDraw() { - getWindow().getDecorView().getViewTreeObserver().removeOnPreDrawListener(this); - setEnteringViews(readyEnteringViews()); - notifySetListener(); - onPrepareRestore(); - return false; + protected void onTakeSharedElements(Bundle sharedElementState) { + setEpicenter(); + // Remove rejected shared elements + ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames); + rejectedNames.removeAll(mSharedElementNames); + ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames); + mListener.handleRejectedSharedElements(rejectedSnapshots); + startRejectedAnimations(rejectedSnapshots); + + // Now start shared element transition + ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState, + mSharedElementNames); + setViewVisibility(mSharedElements, View.VISIBLE); + ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState = + setSharedElementState(sharedElementState, sharedElementSnapshots); + + boolean startEnterTransition = allowOverlappingTransitions(); + boolean startSharedElementTransition = true; + Transition transition = beginTransition(startEnterTransition, startSharedElementTransition); + + if (startEnterTransition) { + startEnterTransition(transition); + } + + setOriginalImageViewState(originalImageViewState); + + if (mResultReceiver != null) { + mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null); + } + mResultReceiver = null; // all done sending messages. } - @Override - public void startExit() { - if (!mExitTransitionStarted) { - mExitTransitionStarted = true; - startExitTransition(mEnteringSharedElementNames); + private Transition beginTransition(boolean startEnterTransition, + boolean startSharedElementTransition) { + Transition sharedElementTransition = null; + if (startSharedElementTransition && !mSharedElementNames.isEmpty()) { + sharedElementTransition = configureTransition(getSharedElementTransition()); } + Transition viewsTransition = null; + if (startEnterTransition && !mTransitioningViews.isEmpty()) { + viewsTransition = configureTransition(getViewsTransition()); + viewsTransition = addTargets(viewsTransition, mTransitioningViews); + } + + Transition transition = mergeTransitions(sharedElementTransition, viewsTransition); + if (transition != null) { + TransitionManager.beginDelayedTransition(getDecor(), transition); + if (startSharedElementTransition && !mSharedElementNames.isEmpty()) { + mSharedElements.get(0).invalidate(); + } else if (startEnterTransition && !mTransitioningViews.isEmpty()) { + mTransitioningViews.get(0).invalidate(); + } + } + return transition; } - @Override - protected Transition getViewsTransition() { - if (!mSupportsTransition) { - return null; + private void startEnterTransition(Transition transition) { + setViewVisibility(mTransitioningViews, View.VISIBLE); + if (!mIsReturning) { + Drawable background = getDecor().getBackground(); + if (background != null) { + background = background.mutate(); + ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255); + animator.setDuration(FADE_BACKGROUND_DURATION_MS); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + makeOpaque(); + } + }); + animator.start(); + } else if (transition != null) { + transition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionEnd(Transition transition) { + transition.removeListener(this); + makeOpaque(); + } + }); + } else { + makeOpaque(); + } } - return getWindow().getEnterTransition(); } - @Override - protected Transition getSharedElementTransition() { - if (!mSupportsTransition) { - return null; + public void stop() { + mHasStopped = true; + mActivity = null; + mIsCanceled = true; + mResultReceiver = null; + } + + private void makeOpaque() { + if (!mHasStopped) { + mActivity.convertFromTranslucent(); + mActivity = null; } - return getWindow().getSharedElementEnterTransition(); } - @Override - protected void onStartEnterTransition(Transition transition, ArrayList<View> enteringViews) { - Drawable background = getDecor().getBackground(); - if (background != null) { - ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255); - animator.setDuration(FADE_BACKGROUND_DURATION_MS); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mMakeOpaque = true; - if (mWasOpaque) { - mActivity.convertFromTranslucent(); - } - } - }); + private boolean allowOverlappingTransitions() { + return mIsReturning ? getWindow().getAllowExitTransitionOverlap() + : getWindow().getAllowEnterTransitionOverlap(); + } + + private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) { + if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) { + return; + } + ViewGroupOverlay overlay = getDecor().getOverlay(); + ObjectAnimator animator = null; + int numRejected = rejectedSnapshots.size(); + for (int i = 0; i < numRejected; i++) { + View snapshot = rejectedSnapshots.get(i); + overlay.add(snapshot); + animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0); animator.start(); - } else if (mWasOpaque) { - transition.addListener(new Transition.TransitionListenerAdapter() { - @Override - public void onTransitionEnd(Transition transition) { - mMakeOpaque = true; - mActivity.convertFromTranslucent(); - } - }); } - super.onStartEnterTransition(transition, enteringViews); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + ViewGroupOverlay overlay = getDecor().getOverlay(); + int numRejected = rejectedSnapshots.size(); + for (int i = 0; i < numRejected; i++) { + overlay.remove(rejectedSnapshots.get(i)); + } + } + }); } - public ArrayList<View> readyEnteringViews() { - ArrayList<View> enteringViews = new ArrayList<View>(); - getDecor().captureTransitioningViews(enteringViews); - if (getViewsTransition() != null) { - setViewVisibility(enteringViews, View.INVISIBLE); + protected void onRemoteExitTransitionComplete() { + if (!allowOverlappingTransitions()) { + boolean startEnterTransition = true; + boolean startSharedElementTransition = false; + Transition transition = beginTransition(startEnterTransition, + startSharedElementTransition); + startEnterTransition(transition); } - return enteringViews; } - @Override - protected void startExitTransition(ArrayList<String> sharedElements) { - mMakeOpaque = false; - notifyPrepareRestore(); + private ArrayList<View> createSnapshots(Bundle state, Collection<String> names) { + int numSharedElements = names.size(); + if (numSharedElements == 0) { + return null; + } + ArrayList<View> snapshots = new ArrayList<View>(numSharedElements); + Context context = getWindow().getContext(); + int[] parentLoc = new int[2]; + getDecor().getLocationOnScreen(parentLoc); + for (String name: names) { + Bundle sharedElementBundle = state.getBundle(name); + if (sharedElementBundle != null) { + Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP); + View snapshot = new View(context); + snapshot.setId(com.android.internal.R.id.shared_element); + Resources resources = getWindow().getContext().getResources(); + snapshot.setBackground(new BitmapDrawable(resources, bitmap)); + snapshot.setViewName(name); + setSharedElementState(snapshot, name, state, parentLoc); + snapshots.add(snapshot); + } + } + return snapshots; + } - if (getDecor().getBackground() == null) { - ColorDrawable black = new ColorDrawable(0xFF000000); - getWindow().setBackgroundDrawable(black); + private static void setSharedElementState(View view, String name, Bundle transitionArgs, + int[] parentLoc) { + Bundle sharedElementBundle = transitionArgs.getBundle(name); + if (sharedElementBundle == null) { + return; } - if (mWasOpaque) { - mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() { - @Override - public void onTranslucentConversionComplete(boolean drawComplete) { - fadeOutBackground(); + + if (view instanceof ImageView) { + int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1); + if (scaleTypeInt >= 0) { + ImageView imageView = (ImageView) view; + ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt]; + imageView.setScaleType(scaleType); + if (scaleType == ImageView.ScaleType.MATRIX) { + float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX); + Matrix matrix = new Matrix(); + matrix.setValues(matrixValues); + imageView.setImageMatrix(matrix); } - }, null); - } else { - fadeOutBackground(); + } } - super.startExitTransition(sharedElements); + float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z); + view.setTranslationZ(z); + + int x = sharedElementBundle.getInt(KEY_SCREEN_X); + int y = sharedElementBundle.getInt(KEY_SCREEN_Y); + int width = sharedElementBundle.getInt(KEY_WIDTH); + int height = sharedElementBundle.getInt(KEY_HEIGHT); + + int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); + int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + view.measure(widthSpec, heightSpec); + + int left = x - parentLoc[0]; + int top = y - parentLoc[1]; + int right = left + width; + int bottom = top + height; + view.layout(left, top, right, bottom); } - private void fadeOutBackground() { - ObjectAnimator animator = ObjectAnimator.ofInt(getDecor().getBackground(), - "alpha", 0); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mBackgroundFadedOut = true; - if (mSharedElementTransitionComplete) { - EnterTransitionCoordinator.super.onSharedElementTransitionEnd(); + private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState( + Bundle sharedElementState, final ArrayList<View> snapshots) { + ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState = + new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>(); + if (sharedElementState != null) { + int[] tempLoc = new int[2]; + for (int i = 0; i < mSharedElementNames.size(); i++) { + View sharedElement = mSharedElements.get(i); + String name = mSharedElementNames.get(i); + Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement, + name, sharedElementState); + if (originalState != null) { + originalImageState.put((ImageView) sharedElement, originalState); } + View parent = (View) sharedElement.getParent(); + parent.getLocationOnScreen(tempLoc); + setSharedElementState(sharedElement, name, sharedElementState, tempLoc); + sharedElement.requestLayout(); } - }); - animator.setDuration(FADE_BACKGROUND_DURATION_MS); - animator.start(); + } + mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots); + + getDecor().getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + getDecor().getViewTreeObserver().removeOnPreDrawListener(this); + mListener.setSharedElementEnd(mSharedElementNames, mSharedElements, + snapshots); + mSharedElementTransitionStarted = true; + return true; + } + } + ); + return originalImageState; } - @Override - protected void onExitTransitionEnd() { - mExitTransitionComplete = true; - exitAfterSharedElementTransition(); - super.onExitTransitionEnd(); + private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name, + Bundle transitionArgs) { + if (!(view instanceof ImageView)) { + return null; + } + Bundle bundle = transitionArgs.getBundle(name); + int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1); + if (scaleTypeInt < 0) { + return null; + } + + ImageView imageView = (ImageView) view; + ImageView.ScaleType originalScaleType = imageView.getScaleType(); + + Matrix originalMatrix = null; + if (originalScaleType == ImageView.ScaleType.MATRIX) { + originalMatrix = new Matrix(imageView.getImageMatrix()); + } + + return Pair.create(originalScaleType, originalMatrix); } - @Override - protected void onSharedElementTransitionEnd() { - mSharedElementTransitionComplete = true; - if (mBackgroundFadedOut) { - super.onSharedElementTransitionEnd(); + private static void setOriginalImageViewState( + ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) { + for (int i = 0; i < originalState.size(); i++) { + ImageView imageView = originalState.keyAt(i); + Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i); + imageView.setScaleType(state.first); + imageView.setImageMatrix(state.second); } } @Override - protected boolean allowOverlappingTransitions() { - return getWindow().getAllowEnterTransitionOverlap(); + protected Transition getViewsTransition() { + return getWindow().getEnterTransition(); } - private void exitAfterSharedElementTransition() { - if (mSharedElementTransitionComplete && mExitTransitionComplete && mBackgroundFadedOut) { - mActivity.finish(); - if (mSupportsTransition) { - mActivity.overridePendingTransition(0, 0); - } - notifyExitTransitionComplete(); - clearConnections(); - } + protected Transition getSharedElementTransition() { + return getWindow().getSharedElementEnterTransition(); } } diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index d920787..43a60a3 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -15,11 +15,20 @@ */ package android.app; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.ColorDrawable; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; import android.transition.Transition; -import android.util.Pair; +import android.transition.TransitionManager; import android.view.View; -import android.view.Window; +import android.widget.ImageView; import java.util.ArrayList; @@ -30,142 +39,245 @@ import java.util.ArrayList; */ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private static final String TAG = "ExitTransitionCoordinator"; + private static final long MAX_WAIT_MS = 1500; - /** - * The Views that have exited and need to be restored to VISIBLE when returning to the - * normal state. - */ - private ArrayList<View> mTransitioningViews; + private boolean mExitComplete; - /** - * Has the exit started? We don't want to accidentally exit multiple times. - */ - private boolean mExitStarted; + private Bundle mSharedElementBundle; - /** - * Has the called Activity's ResultReceiver been set? - */ - private boolean mIsResultReceiverSet; + private boolean mExitNotified; - /** - * Has the exit transition completed? If so, we can notify as soon as the ResultReceiver - * has been set. - */ - private boolean mExitComplete; + private boolean mSharedElementNotified; - /** - * Has the shared element transition completed? If so, we can notify as soon as the - * ResultReceiver has been set. - */ - private Bundle mSharedElements; + private Activity mActivity; - /** - * Has the shared element transition completed? - */ - private boolean mSharedElementsComplete; + private boolean mIsBackgroundReady; + + private boolean mIsReturning; - public ExitTransitionCoordinator(Window window, - ActivityOptions.ActivityTransitionListener listener) { - super(window); - setActivityTransitionListener(listener); + private boolean mIsCanceled; + + private Handler mHandler; + + public ExitTransitionCoordinator(Activity activity, ArrayList<String> names, + ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) { + super(activity.getWindow(), names, accepted, mapped, activity.mTransitionListener); + mIsReturning = isReturning; + mIsBackgroundReady = !mIsReturning; + mActivity = activity; } @Override - protected void onSetResultReceiver() { - mIsResultReceiverSet = true; - notifyCompletions(); + protected void onReceiveResult(int resultCode, Bundle resultData) { + switch (resultCode) { + case MSG_SET_REMOTE_RECEIVER: + mResultReceiver = resultData.getParcelable(KEY_REMOTE_RECEIVER); + if (mIsCanceled) { + mResultReceiver.send(MSG_CANCEL, null); + mResultReceiver = null; + } else { + if (mHandler != null) { + mHandler.removeMessages(MSG_CANCEL); + } + notifyComplete(); + } + break; + case MSG_HIDE_SHARED_ELEMENTS: + if (!mIsCanceled) { + hideSharedElements(); + } + break; + case MSG_START_EXIT_TRANSITION: + startExit(); + break; + case MSG_ACTIVITY_STOPPED: + setViewVisibility(mTransitioningViews, View.VISIBLE); + setViewVisibility(mSharedElements, View.VISIBLE); + break; + } } - @Override - protected void onPrepareRestore() { - makeTransitioningViewsInvisible(); - setEnteringViews(mTransitioningViews); - mTransitioningViews = null; - super.onPrepareRestore(); + private void hideSharedElements() { + setViewVisibility(mSharedElements, View.INVISIBLE); } - @Override - protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) { - super.onTakeSharedElements(sharedElementNames, state); - clearConnections(); + public void startExit() { + beginTransition(); + setViewVisibility(mTransitioningViews, View.INVISIBLE); } - @Override - protected void onActivityStopped() { - if (getViewsTransition() != null) { - setViewVisibility(mTransitioningViews, View.VISIBLE); + public void startExit(int resultCode, Intent data) { + mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + mIsCanceled = true; + mActivity.finish(); + mActivity = null; + } + }; + mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS); + if (getDecor().getBackground() == null) { + ColorDrawable black = new ColorDrawable(0xFF000000); + black.setAlpha(0); + getWindow().setBackgroundDrawable(black); + black.setAlpha(255); } - super.onActivityStopped(); + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this, + mAllSharedElementNames, resultCode, data); + mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() { + @Override + public void onTranslucentConversionComplete(boolean drawComplete) { + if (!mIsCanceled) { + fadeOutBackground(); + } + } + }, options); + startExit(); } - @Override - protected void sharedElementTransitionComplete(Bundle bundle) { - mSharedElements = bundle; - mSharedElementsComplete = true; - notifyCompletions(); + private void fadeOutBackground() { + ObjectAnimator animator = ObjectAnimator.ofInt(getDecor().getBackground(), + "alpha", 0); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIsBackgroundReady = true; + notifyComplete(); + } + }); + animator.setDuration(FADE_BACKGROUND_DURATION_MS); + animator.start(); } - @Override - protected void onExitTransitionEnd() { + private void beginTransition() { + Transition sharedElementTransition = configureTransition(getSharedElementTransition()); + Transition viewsTransition = configureTransition(getViewsTransition()); + viewsTransition = addTargets(viewsTransition, mTransitioningViews); + if (sharedElementTransition == null) { + sharedElementTransitionComplete(); + } else { + sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionEnd(Transition transition) { + sharedElementTransitionComplete(); + } + }); + } + if (viewsTransition == null) { + exitTransitionComplete(); + } else { + viewsTransition.addListener(new Transition.TransitionListenerAdapter() { + @Override + public void onTransitionEnd(Transition transition) { + exitTransitionComplete(); + } + }); + } + + Transition transition = mergeTransitions(sharedElementTransition, viewsTransition); + TransitionManager.beginDelayedTransition(getDecor(), transition); + } + + private void exitTransitionComplete() { mExitComplete = true; - notifyCompletions(); - super.onExitTransitionEnd(); + notifyComplete(); } - private void notifyCompletions() { - if (mIsResultReceiverSet && mSharedElementsComplete) { - if (mSharedElements != null) { - notifySharedElementTransitionComplete(mSharedElements); - mSharedElements = null; - } - if (mExitComplete) { - notifyExitTransitionComplete(); - } - } + protected boolean isReadyToNotify() { + return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady; } - @Override - public void startExit() { - if (!mExitStarted) { - mExitStarted = true; - setSharedElements(); - startExitTransition(getSharedElementNames()); + private void sharedElementTransitionComplete() { + Bundle bundle = new Bundle(); + int[] tempLoc = new int[2]; + for (int i = 0; i < mSharedElementNames.size(); i++) { + View sharedElement = mSharedElements.get(i); + String name = mSharedElementNames.get(i); + captureSharedElementState(sharedElement, name, bundle, tempLoc); } + mSharedElementBundle = bundle; + notifyComplete(); } - @Override - protected Transition getViewsTransition() { - if (!getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) { - return null; + protected void notifyComplete() { + if (isReadyToNotify()) { + if (!mSharedElementNotified) { + mSharedElementNotified = true; + mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle); + } + if (!mExitNotified && mExitComplete) { + mExitNotified = true; + mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null); + mResultReceiver = null; // done talking + if (mIsReturning) { + mActivity.finish(); + mActivity.overridePendingTransition(0, 0); + } + mActivity = null; + } } - return getWindow().getExitTransition(); } - @Override - protected Transition getSharedElementTransition() { - if (!getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) { - return null; + /** + * Captures placement information for Views with a shared element name for + * Activity Transitions. + * + * @param view The View to capture the placement information for. + * @param name The shared element name in the target Activity to apply the placement + * information for. + * @param transitionArgs Bundle to store shared element placement information. + * @param tempLoc A temporary int[2] for capturing the current location of views. + */ + private static void captureSharedElementState(View view, String name, Bundle transitionArgs, + int[] tempLoc) { + Bundle sharedElementBundle = new Bundle(); + view.getLocationOnScreen(tempLoc); + float scaleX = view.getScaleX(); + sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]); + int width = Math.round(view.getWidth() * scaleX); + sharedElementBundle.putInt(KEY_WIDTH, width); + + float scaleY = view.getScaleY(); + sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]); + int height = Math.round(view.getHeight() * scaleY); + sharedElementBundle.putInt(KEY_HEIGHT, height); + + sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ()); + + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + view.draw(canvas); + sharedElementBundle.putParcelable(KEY_BITMAP, bitmap); + + if (view instanceof ImageView) { + ImageView imageView = (ImageView) view; + int scaleTypeInt = scaleTypeToInt(imageView.getScaleType()); + sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt); + if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) { + float[] matrix = new float[9]; + imageView.getImageMatrix().getValues(matrix); + sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix); + } } - return getWindow().getSharedElementExitTransition(); + + transitionArgs.putBundle(name, sharedElementBundle); } - private void makeTransitioningViewsInvisible() { - if (getViewsTransition() != null) { - setViewVisibility(mTransitioningViews, View.INVISIBLE); + private static int scaleTypeToInt(ImageView.ScaleType scaleType) { + for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) { + if (scaleType == SCALE_TYPE_VALUES[i]) { + return i; + } } + return -1; } @Override - protected void onStartExitTransition(ArrayList<View> exitingViews) { - mTransitioningViews = new ArrayList<View>(); - if (exitingViews != null) { - mTransitioningViews.addAll(exitingViews); - } - mTransitioningViews.addAll(getSharedElements()); + protected Transition getViewsTransition() { + return getWindow().getExitTransition(); } - @Override - protected boolean allowOverlappingTransitions() { - return getWindow().getAllowExitTransitionOverlap(); + protected Transition getSharedElementTransition() { + return getWindow().getSharedElementExitTransition(); } } diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java index e4de7af..ab0fc66 100644 --- a/core/java/android/app/FragmentBreadCrumbs.java +++ b/core/java/android/app/FragmentBreadCrumbs.java @@ -37,7 +37,10 @@ import android.widget.TextView; * * <p>The default style for this view is * {@link android.R.style#Widget_FragmentBreadCrumbs}. + * + * @deprecated This widget is no longer supported. */ +@Deprecated public class FragmentBreadCrumbs extends ViewGroup implements FragmentManager.OnBackStackChangedListener { Activity mActivity; @@ -88,6 +91,9 @@ public class FragmentBreadCrumbs extends ViewGroup this(context, attrs, defStyleAttr, 0); } + /** + * @hide + */ public FragmentBreadCrumbs( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index d259b30..8753312 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -112,6 +112,7 @@ public interface IActivityManager extends IInterface { public void activityDestroyed(IBinder token) throws RemoteException; public String getCallingPackage(IBinder token) throws RemoteException; public ComponentName getCallingActivity(IBinder token) throws RemoteException; + public List<IAppTask> getAppTasks() throws RemoteException; public List<RunningTaskInfo> getTasks(int maxNum, int flags) throws RemoteException; public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) throws RemoteException; @@ -741,4 +742,5 @@ public interface IActivityManager extends IInterface { int SET_RECENTS_ACTIVITY_VALUES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217; int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218; int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219; + int GET_APP_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+220; } diff --git a/core/java/android/app/IAppTask.aidl b/core/java/android/app/IAppTask.aidl new file mode 100644 index 0000000..268b4dd --- /dev/null +++ b/core/java/android/app/IAppTask.aidl @@ -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. + */ + +package android.app; + +import android.app.ActivityManager; + +/** @hide */ +interface IAppTask { + void finishAndRemoveTask(); + ActivityManager.RecentTaskInfo getTaskInfo(); +} diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 76a6a8e..fd76b9c4 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -16,9 +16,6 @@ package android.app; -import com.android.internal.R; -import com.android.internal.util.NotificationColorUtil; - import android.annotation.IntDef; import android.content.Context; import android.content.Intent; @@ -41,6 +38,9 @@ import android.view.View; import android.widget.ProgressBar; import android.widget.RemoteViews; +import com.android.internal.R; +import com.android.internal.util.NotificationColorUtil; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.text.NumberFormat; @@ -134,7 +134,7 @@ public class Notification implements Parcelable * leave it at its default value of 0. * * @see android.widget.ImageView#setImageLevel - * @see android.graphics.drawable#setLevel + * @see android.graphics.drawable.Drawable#setLevel */ public int iconLevel; @@ -700,10 +700,13 @@ public class Notification implements Parcelable * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is * selected by the user. * <p> - * Apps should use {@link Builder#addAction(int, CharSequence, PendingIntent)} to create and - * attach actions. + * Apps should use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)} + * or {@link Notification.Builder#addAction(Notification.Action)} + * to attach actions. */ public static class Action implements Parcelable { + private final Bundle mExtras; + /** * Small icon representing the action. */ @@ -717,22 +720,102 @@ public class Notification implements Parcelable * may be rendered in a disabled presentation by the system UI. */ public PendingIntent actionIntent; - - private Action() { } + private Action(Parcel in) { icon = in.readInt(); title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); if (in.readInt() == 1) { actionIntent = PendingIntent.CREATOR.createFromParcel(in); } + mExtras = in.readBundle(); } /** - * Use {@link Builder#addAction(int, CharSequence, PendingIntent)}. + * Use {@link Notification.Builder#addAction(int, CharSequence, PendingIntent)}. */ public Action(int icon, CharSequence title, PendingIntent intent) { + this(icon, title, intent, new Bundle()); + } + + private Action(int icon, CharSequence title, PendingIntent intent, Bundle extras) { this.icon = icon; this.title = title; this.actionIntent = intent; + this.mExtras = extras != null ? extras : new Bundle(); + } + + /** + * Get additional metadata carried around with this Action. + */ + public Bundle getExtras() { + return mExtras; + } + + /** + * Builder class for {@link Action} objects. + */ + public static class Builder { + private final int mIcon; + private final CharSequence mTitle; + private final PendingIntent mIntent; + private final Bundle mExtras; + + /** + * Construct a new builder for {@link Action} object. + * @param icon icon to show for this action + * @param title the title of the action + * @param intent the {@link PendingIntent} to fire when users trigger this action + */ + public Builder(int icon, CharSequence title, PendingIntent intent) { + this(icon, title, intent, new Bundle()); + } + + /** + * Construct a new builder for {@link Action} object using the fields from an + * {@link Action}. + * @param action the action to read fields from. + */ + public Builder(Action action) { + this(action.icon, action.title, action.actionIntent, new Bundle(action.mExtras)); + } + + private Builder(int icon, CharSequence title, PendingIntent intent, Bundle extras) { + mIcon = icon; + mTitle = title; + mIntent = intent; + mExtras = extras; + } + + /** + * Merge additional metadata into this builder. + * + * <p>Values within the Bundle will replace existing extras values in this Builder. + * + * @see Notification.Action#extras + */ + public Builder addExtras(Bundle extras) { + if (extras != null) { + mExtras.putAll(extras); + } + return this; + } + + /** + * Get the metadata Bundle used by this Builder. + * + * <p>The returned Bundle is shared with this Builder. + */ + public Bundle getExtras() { + return mExtras; + } + + /** + * Combine all of the options that have been set and return a new {@link Action} + * object. + * @return the built action + */ + public Action build() { + return new Action(mIcon, mTitle, mIntent, mExtras); + } } @Override @@ -740,8 +823,8 @@ public class Notification implements Parcelable return new Action( this.icon, this.title, - this.actionIntent // safe to alias - ); + this.actionIntent, // safe to alias + new Bundle(this.mExtras)); } @Override public int describeContents() { @@ -757,9 +840,10 @@ public class Notification implements Parcelable } else { out.writeInt(0); } + out.writeBundle(mExtras); } - public static final Parcelable.Creator<Action> CREATOR - = new Parcelable.Creator<Action>() { + public static final Parcelable.Creator<Action> CREATOR = + new Parcelable.Creator<Action>() { public Action createFromParcel(Parcel in) { return new Action(in); } @@ -1372,7 +1456,7 @@ public class Notification implements Parcelable /** * Add a timestamp pertaining to the notification (usually the time the event occurred). * It will be shown in the notification content view by default; use - * {@link Builder#setShowWhen(boolean) setShowWhen} to control this. + * {@link #setShowWhen(boolean) setShowWhen} to control this. * * @see Notification#when */ @@ -1382,7 +1466,7 @@ public class Notification implements Parcelable } /** - * Control whether the timestamp set with {@link Builder#setWhen(long) setWhen} is shown + * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown * in the content view. */ public Builder setShowWhen(boolean show) { @@ -1761,11 +1845,13 @@ public class Notification implements Parcelable * * @see Notification#extras */ - public Builder addExtras(Bundle bag) { - if (mExtras == null) { - mExtras = new Bundle(bag); - } else { - mExtras.putAll(bag); + public Builder addExtras(Bundle extras) { + if (extras != null) { + if (mExtras == null) { + mExtras = new Bundle(extras); + } else { + mExtras.putAll(extras); + } } return this; } @@ -1782,8 +1868,8 @@ public class Notification implements Parcelable * * @see Notification#extras */ - public Builder setExtras(Bundle bag) { - mExtras = bag; + public Builder setExtras(Bundle extras) { + mExtras = extras; return this; } @@ -1827,6 +1913,26 @@ public class Notification implements Parcelable } /** + * Add an action to this notification. Actions are typically displayed by + * the system as a button adjacent to the notification content. + * <p> + * Every action must have an icon (32dp square and matching the + * <a href="{@docRoot}design/style/iconography.html#action-bar">Holo + * Dark action bar</a> visual style), a textual label, and a {@link PendingIntent}. + * <p> + * A notification in its expanded form can display up to 3 actions, from left to right in + * the order they were added. Actions will not be displayed when the notification is + * collapsed, however, so be sure that any essential functions may be accessed by the user + * in some other way (for example, in the Activity pointed to by {@link #contentIntent}). + * + * @param action The action to add. + */ + public Builder addAction(Action action) { + mActions.add(action); + return this; + } + + /** * Add a rich notification style to be applied at build time. * * @param style Object responsible for modifying the notification style. @@ -1889,26 +1995,20 @@ public class Notification implements Parcelable RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId); boolean showLine3 = false; boolean showLine2 = false; - int smallIconImageViewId = R.id.icon; + if (mPriority < PRIORITY_LOW) { // TODO: Low priority presentation } if (mLargeIcon != null) { contentView.setImageViewBitmap(R.id.icon, mLargeIcon); processLargeIcon(mLargeIcon, contentView); - smallIconImageViewId = R.id.right_icon; - } - if (mSmallIcon != 0) { - contentView.setImageViewResource(smallIconImageViewId, mSmallIcon); - contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE); - if (mLargeIcon != null) { - processSmallRightIcon(mSmallIcon, smallIconImageViewId, contentView); - } else { - processSmallIconAsLarge(mSmallIcon, contentView); - } - - } else { - contentView.setViewVisibility(smallIconImageViewId, View.GONE); + contentView.setImageViewResource(R.id.right_icon, mSmallIcon); + contentView.setViewVisibility(R.id.right_icon, View.VISIBLE); + processSmallRightIcon(mSmallIcon, contentView); + } else { // small icon at left + contentView.setImageViewResource(R.id.icon, mSmallIcon); + contentView.setViewVisibility(R.id.icon, View.VISIBLE); + processSmallIconAsLarge(mSmallIcon, contentView); } if (mContentTitle != null) { contentView.setTextViewText(R.id.title, processLegacyText(mContentTitle)); @@ -2103,6 +2203,8 @@ public class Notification implements Parcelable private void processLargeIcon(Bitmap largeIcon, RemoteViews contentView) { if (!isLegacy() || mColorUtil.isGrayscale(largeIcon)) { applyLargeIconBackground(contentView); + } else { + removeLargeIconBackground(contentView); } } @@ -2122,16 +2224,31 @@ public class Notification implements Parcelable -1); } + private void removeLargeIconBackground(RemoteViews contentView) { + contentView.setInt(R.id.icon, "setBackgroundResource", 0); + } + /** * Recolor small icons when used in the R.id.right_icon slot. */ - private void processSmallRightIcon(int smallIconDrawableId, int smallIconImageViewId, + private void processSmallRightIcon(int smallIconDrawableId, RemoteViews contentView) { if (!isLegacy() || mColorUtil.isGrayscale(mContext, smallIconDrawableId)) { - contentView.setDrawableParameters(smallIconImageViewId, false, -1, - mContext.getResources().getColor( - R.color.notification_action_legacy_color_filter), - PorterDuff.Mode.MULTIPLY, -1); + contentView.setDrawableParameters(R.id.right_icon, false, -1, + 0xFFFFFFFF, + PorterDuff.Mode.SRC_ATOP, -1); + + contentView.setInt(R.id.right_icon, + "setBackgroundResource", + R.drawable.notification_icon_legacy_bg); + + contentView.setDrawableParameters( + R.id.right_icon, + true, + -1, + mColor, + PorterDuff.Mode.SRC_ATOP, + -1); } } diff --git a/core/java/android/app/PackageInstallObserver.java b/core/java/android/app/PackageInstallObserver.java index dacffb4..941efbd 100644 --- a/core/java/android/app/PackageInstallObserver.java +++ b/core/java/android/app/PackageInstallObserver.java @@ -18,32 +18,36 @@ package android.app; import android.content.pm.IPackageInstallObserver2; import android.os.Bundle; -import android.os.RemoteException; -/** - * @hide - * - * New-style observer for package installers to use. - */ +/** {@hide} */ public class PackageInstallObserver { - IPackageInstallObserver2.Stub mObserver = new IPackageInstallObserver2.Stub() { + private final IPackageInstallObserver2.Stub mBinder = new IPackageInstallObserver2.Stub() { @Override - public void packageInstalled(String pkgName, Bundle extras, int result) - throws RemoteException { - PackageInstallObserver.this.packageInstalled(pkgName, extras, result); + public void packageInstalled(String basePackageName, Bundle extras, int returnCode) { + PackageInstallObserver.this.packageInstalled(basePackageName, extras, returnCode); } }; + /** {@hide} */ + public IPackageInstallObserver2.Stub getBinder() { + return mBinder; + } + /** - * This method will be called to report the result of the package installation attempt. + * This method will be called to report the result of the package + * installation attempt. * - * @param pkgName Name of the package whose installation was attempted - * @param extras If non-null, this Bundle contains extras providing additional information - * about an install failure. See {@link android.content.pm.PackageManager} for - * documentation about which extras apply to various failures; in particular the - * strings named EXTRA_FAILURE_*. - * @param result The numeric success or failure code indicating the basic outcome + * @param basePackageName Name of the package whose installation was + * attempted + * @param extras If non-null, this Bundle contains extras providing + * additional information about an install failure. See + * {@link android.content.pm.PackageManager} for documentation + * about which extras apply to various failures; in particular + * the strings named EXTRA_FAILURE_*. + * @param returnCode The numeric success or failure code indicating the + * basic outcome + * @hide */ - public void packageInstalled(String pkgName, Bundle extras, int result) { + public void packageInstalled(String basePackageName, Bundle extras, int returnCode) { } } diff --git a/core/java/android/app/PackageUninstallObserver.java b/core/java/android/app/PackageUninstallObserver.java new file mode 100644 index 0000000..0a960a7 --- /dev/null +++ b/core/java/android/app/PackageUninstallObserver.java @@ -0,0 +1,37 @@ +/* + * 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; + +import android.content.pm.IPackageDeleteObserver; + +/** {@hide} */ +public class PackageUninstallObserver { + private final IPackageDeleteObserver.Stub mBinder = new IPackageDeleteObserver.Stub() { + @Override + public void packageDeleted(String basePackageName, int returnCode) { + PackageUninstallObserver.this.onUninstallFinished(basePackageName, returnCode); + } + }; + + /** {@hide} */ + public IPackageDeleteObserver.Stub getBinder() { + return mBinder; + } + + public void onUninstallFinished(String basePackageName, int returnCode) { + } +} diff --git a/core/java/android/app/SharedElementListener.java b/core/java/android/app/SharedElementListener.java new file mode 100644 index 0000000..d4bc019 --- /dev/null +++ b/core/java/android/app/SharedElementListener.java @@ -0,0 +1,87 @@ +/* + * 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; + +import android.view.View; + +import java.util.List; +import java.util.Map; + +/** + * Listener provided in + * {@link Activity#setSharedElementListener(SharedElementListener)} + * to monitor the Activity transitions. The events can be used to customize or override Activity + * Transition behavior. + */ +public class SharedElementListener { + /** + * Called to allow the listener to customize the start state of the shared element for + * the shared element entering transition. By default, the shared element is placed in + * the position and with the size of the shared element in the calling Activity or Fragment. + * + * @param sharedElementNames The names of the shared elements that were accepted into + * the View hierarchy. + * @param sharedElements The shared elements that are part of the View hierarchy. + * @param sharedElementSnapshots The Views containing snap shots of the shared element + * from the launching Window. These elements will not + * be part of the scene, but will be positioned relative + * to the Window decor View. + */ + public void setSharedElementStart(List<String> sharedElementNames, + List<View> sharedElements, List<View> sharedElementSnapshots) {} + + /** + * Called to allow the listener to customize the end state of the shared element for + * the shared element entering transition. + * + * @param sharedElementNames The names of the shared elements that were accepted into + * the View hierarchy. + * @param sharedElements The shared elements that are part of the View hierarchy. + * @param sharedElementSnapshots The Views containing snap shots of the shared element + * from the launching Window. These elements will not + * be part of the scene, but will be positioned relative + * to the Window decor View. + */ + public void setSharedElementEnd(List<String> sharedElementNames, + List<View> sharedElements, List<View> sharedElementSnapshots) {} + + /** + * If nothing is done, all shared elements that were not accepted by + * {@link #remapSharedElements(java.util.List, java.util.Map)} will be Transitioned + * out of the entering scene automatically. Any elements removed from + * rejectedSharedElements must be handled by the ActivityTransitionListener. + * <p>Views in rejectedSharedElements will have their position and size set to the + * position of the calling shared element, relative to the Window decor View. This + * view may be safely added to the decor View's overlay to remain in position.</p> + * + * @param rejectedSharedElements Views containing visual information of shared elements + * that are not part of the entering scene. These Views + * are positioned relative to the Window decor View. A + * View removed from this list will not be transitioned + * automatically. + */ + public void handleRejectedSharedElements(List<View> rejectedSharedElements) {} + + /** + * Lets the ActivityTransitionListener adjust the mapping of shared element names to + * Views. + * @param names The names of all shared elements transferred from the calling Activity + * to the started Activity. + * @param sharedElements The mapping of shared element names to Views. The best guess + * will be filled into sharedElements based on the View names. + */ + public void remapSharedElements(List<String> names, Map<String, View> sharedElements) {} +} diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index 49b156d..3dd7094 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -31,6 +31,8 @@ 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 stopScan(in int appIf, in boolean isServer); void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 0d1b262..6b44a11 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -453,7 +453,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * * {@hide} */ - public String requiredCpuAbi; + public String cpuAbi; /** * The kernel user-ID that has been assigned to this application; @@ -592,7 +592,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sourceDir = orig.sourceDir; publicSourceDir = orig.publicSourceDir; nativeLibraryDir = orig.nativeLibraryDir; - requiredCpuAbi = orig.requiredCpuAbi; + cpuAbi = orig.cpuAbi; resourceDirs = orig.resourceDirs; seinfo = orig.seinfo; sharedLibraryFiles = orig.sharedLibraryFiles; @@ -634,7 +634,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString(sourceDir); dest.writeString(publicSourceDir); dest.writeString(nativeLibraryDir); - dest.writeString(requiredCpuAbi); + dest.writeString(cpuAbi); dest.writeStringArray(resourceDirs); dest.writeString(seinfo); dest.writeStringArray(sharedLibraryFiles); @@ -675,7 +675,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { sourceDir = source.readString(); publicSourceDir = source.readString(); nativeLibraryDir = source.readString(); - requiredCpuAbi = source.readString(); + cpuAbi = source.readString(); resourceDirs = source.readStringArray(); seinfo = source.readString(); sharedLibraryFiles = source.readStringArray(); diff --git a/core/java/android/content/pm/ContainerEncryptionParams.java b/core/java/android/content/pm/ContainerEncryptionParams.java index 88112a7..dd1332b 100644 --- a/core/java/android/content/pm/ContainerEncryptionParams.java +++ b/core/java/android/content/pm/ContainerEncryptionParams.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.PrivateApi; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -31,8 +32,11 @@ import javax.crypto.spec.IvParameterSpec; /** * Represents encryption parameters used to read a container. * + * @deprecated encrypted containers are legacy. * @hide */ +@PrivateApi +@Deprecated public class ContainerEncryptionParams implements Parcelable { protected static final String TAG = "ContainerEncryptionParams"; diff --git a/core/java/android/content/pm/IPackageInstallObserver2.aidl b/core/java/android/content/pm/IPackageInstallObserver2.aidl index 2602ab5..7205ce7 100644 --- a/core/java/android/content/pm/IPackageInstallObserver2.aidl +++ b/core/java/android/content/pm/IPackageInstallObserver2.aidl @@ -1,22 +1,22 @@ /* -** -** Copyright 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. -*/ + * 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.content.pm; +import android.content.IntentSender; import android.os.Bundle; /** @@ -40,6 +40,5 @@ oneway interface IPackageInstallObserver2 { * </tr> * </table> */ - void packageInstalled(in String packageName, in Bundle extras, int returnCode); + void packageInstalled(String basePackageName, in Bundle extras, int returnCode); } - diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl new file mode 100644 index 0000000..68c019b --- /dev/null +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -0,0 +1,33 @@ +/* + * 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.content.pm; + +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.IPackageInstallerSession; +import android.content.pm.PackageInstallerParams; +import android.os.ParcelFileDescriptor; + +/** {@hide} */ +interface IPackageInstaller { + int createSession(int userId, String installerPackageName, in PackageInstallerParams params); + IPackageInstallerSession openSession(int sessionId); + + int[] getSessions(int userId, String installerPackageName); + + void uninstall(int userId, String basePackageName, in IPackageDeleteObserver observer); + void uninstallSplit(int userId, String basePackageName, String splitName, in IPackageDeleteObserver observer); +} diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl new file mode 100644 index 0000000..f881acd --- /dev/null +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -0,0 +1,30 @@ +/* + * 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.content.pm; + +import android.content.pm.IPackageInstallObserver2; +import android.os.ParcelFileDescriptor; + +/** {@hide} */ +interface IPackageInstallerSession { + void updateProgress(int progress); + + ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes); + + void install(in IPackageInstallObserver2 observer); + void destroy(); +} diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 03eb50f..6cb781f 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -26,6 +26,7 @@ import android.content.pm.ContainerEncryptionParams; import android.content.pm.FeatureInfo; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageInstallObserver2; +import android.content.pm.IPackageInstaller; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageMoveObserver; @@ -450,4 +451,6 @@ interface IPackageManager { boolean setApplicationBlockedSettingAsUser(String packageName, boolean blocked, int userId); boolean getApplicationBlockedSettingAsUser(String packageName, int userId); + + IPackageInstaller getPackageInstaller(); } diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java index 409b5ae..943534f 100644 --- a/core/java/android/content/pm/ManifestDigest.java +++ b/core/java/android/content/pm/ManifestDigest.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.PrivateApi; import android.os.Parcel; import android.os.Parcelable; import android.util.Slog; @@ -36,6 +37,7 @@ import libcore.io.IoUtils; * * @hide */ +@PrivateApi public class ManifestDigest implements Parcelable { private static final String TAG = "ManifestDigest"; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java new file mode 100644 index 0000000..d7bd473 --- /dev/null +++ b/core/java/android/content/pm/PackageInstaller.java @@ -0,0 +1,159 @@ +/* + * 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.content.pm; + +import android.app.PackageInstallObserver; +import android.app.PackageUninstallObserver; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; + +/** {@hide} */ +public class PackageInstaller { + private final PackageManager mPm; + private final IPackageInstaller mInstaller; + private final int mUserId; + private final String mInstallerPackageName; + + /** {@hide} */ + public PackageInstaller(PackageManager pm, IPackageInstaller installer, int userId, + String installerPackageName) { + mPm = pm; + mInstaller = installer; + mUserId = userId; + mInstallerPackageName = installerPackageName; + } + + public boolean isPackageAvailable(String basePackageName) { + try { + final ApplicationInfo info = mPm.getApplicationInfo(basePackageName, + PackageManager.GET_UNINSTALLED_PACKAGES); + return ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0); + } catch (NameNotFoundException e) { + return false; + } + } + + public void installAvailablePackage(String basePackageName, PackageInstallObserver observer) { + int returnCode; + try { + returnCode = mPm.installExistingPackage(basePackageName); + } catch (NameNotFoundException e) { + returnCode = PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; + } + observer.packageInstalled(basePackageName, null, returnCode); + } + + public int createSession(PackageInstallerParams params) { + try { + return mInstaller.createSession(mUserId, mInstallerPackageName, params); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + public Session openSession(int sessionId) { + try { + return new Session(mInstaller.openSession(sessionId)); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + public int[] getSessions() { + try { + return mInstaller.getSessions(mUserId, mInstallerPackageName); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + public void uninstall(String basePackageName, PackageUninstallObserver observer) { + try { + mInstaller.uninstall(mUserId, basePackageName, observer.getBinder()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + public void uninstall(String basePackageName, String splitName, + PackageUninstallObserver observer) { + try { + mInstaller.uninstallSplit(mUserId, basePackageName, splitName, observer.getBinder()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** + * An installation that is being actively staged. For an install to succeed, + * all existing and new packages must have identical package names, version + * codes, and signing certificates. + * <p> + * A session may contain any number of split packages. If the application + * does not yet exist, this session must include a base package. + * <p> + * If a package included in this session is already defined by the existing + * installation (for example, the same split name), the package in this + * session will replace the existing package. + */ + public class Session { + private IPackageInstallerSession mSession; + + /** {@hide} */ + public Session(IPackageInstallerSession session) { + mSession = session; + } + + public void updateProgress(int progress) { + try { + mSession.updateProgress(progress); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + public ParcelFileDescriptor openWrite(String overlayName, long offsetBytes, + long lengthBytes) { + try { + return mSession.openWrite(overlayName, offsetBytes, lengthBytes); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + public void install(PackageInstallObserver observer) { + try { + mSession.install(observer.getBinder()); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + public void close() { + // No resources to release at the moment + } + + public void destroy() { + try { + mSession.destroy(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + } +} diff --git a/core/java/android/content/pm/PackageInstallerParams.aidl b/core/java/android/content/pm/PackageInstallerParams.aidl new file mode 100644 index 0000000..b3dde21 --- /dev/null +++ b/core/java/android/content/pm/PackageInstallerParams.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.content.pm; + +parcelable PackageInstallerParams; diff --git a/core/java/android/content/pm/PackageInstallerParams.java b/core/java/android/content/pm/PackageInstallerParams.java new file mode 100644 index 0000000..67cf276 --- /dev/null +++ b/core/java/android/content/pm/PackageInstallerParams.java @@ -0,0 +1,141 @@ +/* + * 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.content.pm; + +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Parameters that define an installation session. + * + * {@hide} + */ +public class PackageInstallerParams implements Parcelable { + + // TODO: extend to support remaining VerificationParams + + /** {@hide} */ + public boolean fullInstall; + /** {@hide} */ + public int installFlags; + /** {@hide} */ + public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY; + /** {@hide} */ + public Signature[] signatures; + /** {@hide} */ + public long deltaSize = -1; + /** {@hide} */ + public Bitmap icon; + /** {@hide} */ + public String title; + /** {@hide} */ + public Uri originatingUri; + /** {@hide} */ + public Uri referrerUri; + + public PackageInstallerParams() { + } + + /** {@hide} */ + public PackageInstallerParams(Parcel source) { + this.fullInstall = source.readInt() != 0; + this.installFlags = source.readInt(); + this.installLocation = source.readInt(); + this.signatures = (Signature[]) source.readParcelableArray(null); + deltaSize = source.readLong(); + if (source.readInt() != 0) { + icon = Bitmap.CREATOR.createFromParcel(source); + } + title = source.readString(); + originatingUri = Uri.CREATOR.createFromParcel(source); + referrerUri = Uri.CREATOR.createFromParcel(source); + } + + public void setFullInstall(boolean fullInstall) { + this.fullInstall = fullInstall; + } + + public void setInstallFlags(int installFlags) { + this.installFlags = installFlags; + } + + public void setInstallLocation(int installLocation) { + this.installLocation = installLocation; + } + + public void setSignatures(Signature[] signatures) { + this.signatures = signatures; + } + + public void setDeltaSize(long deltaSize) { + this.deltaSize = deltaSize; + } + + public void setIcon(Bitmap icon) { + this.icon = icon; + } + + public void setTitle(CharSequence title) { + this.title = (title != null) ? title.toString() : null; + } + + public void setOriginatingUri(Uri originatingUri) { + this.originatingUri = originatingUri; + } + + public void setReferrerUri(Uri referrerUri) { + this.referrerUri = referrerUri; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(fullInstall ? 1 : 0); + dest.writeInt(installFlags); + dest.writeInt(installLocation); + dest.writeParcelableArray(signatures, flags); + dest.writeLong(deltaSize); + if (icon != null) { + dest.writeInt(1); + icon.writeToParcel(dest, flags); + } else { + dest.writeInt(0); + } + dest.writeString(title); + dest.writeParcelable(originatingUri, flags); + dest.writeParcelable(referrerUri, flags); + } + + public static final Parcelable.Creator<PackageInstallerParams> + CREATOR = new Parcelable.Creator<PackageInstallerParams>() { + @Override + public PackageInstallerParams createFromParcel(Parcel p) { + return new PackageInstallerParams(p); + } + + @Override + public PackageInstallerParams[] newArray(int size) { + return new PackageInstallerParams[size]; + } + }; +} diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index eb2c11f..0e2eab7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -17,6 +17,7 @@ package android.content.pm; import android.annotation.IntDef; +import android.annotation.PrivateApi; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.PackageInstallObserver; @@ -369,6 +370,7 @@ public abstract class PackageManager { * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} on success. * @hide */ + @PrivateApi public static final int INSTALL_SUCCEEDED = 1; /** @@ -377,6 +379,7 @@ public abstract class PackageManager { * already installed. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_ALREADY_EXISTS = -1; /** @@ -385,6 +388,7 @@ public abstract class PackageManager { * file is invalid. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_INVALID_APK = -2; /** @@ -393,6 +397,7 @@ public abstract class PackageManager { * is invalid. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_INVALID_URI = -3; /** @@ -401,6 +406,7 @@ public abstract class PackageManager { * service found that the device didn't have enough storage space to install the app. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4; /** @@ -409,6 +415,7 @@ public abstract class PackageManager { * package is already installed with the same name. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5; /** @@ -417,6 +424,7 @@ public abstract class PackageManager { * the requested shared user does not exist. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_NO_SHARED_USER = -6; /** @@ -426,6 +434,7 @@ public abstract class PackageManager { * than the new package (and the old package's data was not removed). * @hide */ + @PrivateApi public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7; /** @@ -435,6 +444,7 @@ public abstract class PackageManager { * device and does not have matching signature. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8; /** @@ -443,6 +453,7 @@ public abstract class PackageManager { * the new package uses a shared library that is not available. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9; /** @@ -451,6 +462,7 @@ public abstract class PackageManager { * the new package uses a shared library that is not available. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10; /** @@ -460,6 +472,7 @@ public abstract class PackageManager { * either because there was not enough storage or the validation failed. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_DEXOPT = -11; /** @@ -469,6 +482,7 @@ public abstract class PackageManager { * that required by the package. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_OLDER_SDK = -12; /** @@ -478,6 +492,7 @@ public abstract class PackageManager { * same authority as a provider already installed in the system. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13; /** @@ -487,6 +502,7 @@ public abstract class PackageManager { * that required by the package. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_NEWER_SDK = -14; /** @@ -497,6 +513,7 @@ public abstract class PackageManager { * flag. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_TEST_ONLY = -15; /** @@ -506,6 +523,7 @@ public abstract class PackageManager { * compatible with the the device's CPU_ABI. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16; /** @@ -514,6 +532,7 @@ public abstract class PackageManager { * the new package uses a feature that is not available. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_MISSING_FEATURE = -17; // ------ Errors related to sdcard @@ -523,6 +542,7 @@ public abstract class PackageManager { * a secure container mount point couldn't be accessed on external media. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_CONTAINER_ERROR = -18; /** @@ -532,6 +552,7 @@ public abstract class PackageManager { * location. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19; /** @@ -541,6 +562,7 @@ public abstract class PackageManager { * location because the media is not available. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20; /** @@ -549,6 +571,7 @@ public abstract class PackageManager { * the new package couldn't be installed because the verification timed out. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21; /** @@ -557,6 +580,7 @@ public abstract class PackageManager { * the new package couldn't be installed because the verification did not succeed. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22; /** @@ -565,6 +589,7 @@ public abstract class PackageManager { * the package changed from what the calling program expected. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23; /** @@ -590,6 +615,7 @@ public abstract class PackageManager { * '.apk' extension. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_NOT_APK = -100; /** @@ -598,6 +624,7 @@ public abstract class PackageManager { * if the parser was unable to retrieve the AndroidManifest.xml file. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101; /** @@ -606,6 +633,7 @@ public abstract class PackageManager { * if the parser encountered an unexpected exception. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; /** @@ -614,6 +642,7 @@ public abstract class PackageManager { * if the parser did not find any certificates in the .apk. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; /** @@ -622,6 +651,7 @@ public abstract class PackageManager { * if the parser found inconsistent certificates on the files in the .apk. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104; /** @@ -631,6 +661,7 @@ public abstract class PackageManager { * files in the .apk. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105; /** @@ -639,6 +670,7 @@ public abstract class PackageManager { * if the parser encountered a bad or missing package name in the manifest. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106; /** @@ -647,6 +679,7 @@ public abstract class PackageManager { * if the parser encountered a bad shared user id name in the manifest. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107; /** @@ -655,6 +688,7 @@ public abstract class PackageManager { * if the parser encountered some structural problem in the manifest. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108; /** @@ -664,6 +698,7 @@ public abstract class PackageManager { * in the manifest. * @hide */ + @PrivateApi public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109; /** @@ -672,6 +707,7 @@ public abstract class PackageManager { * if the system failed to install the package because of system issues. * @hide */ + @PrivateApi public static final int INSTALL_FAILED_INTERNAL_ERROR = -110; /** @@ -2863,6 +2899,7 @@ public abstract class PackageManager { * instead. This method will continue to be supported but the older observer interface * will not get additional failure details. */ + // @PrivateApi public abstract void installPackage( Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName); @@ -2897,6 +2934,7 @@ public abstract class PackageManager { * continue to be supported but the older observer interface will not get additional failure * details. */ + // @PrivateApi public abstract void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest, @@ -3025,6 +3063,7 @@ public abstract class PackageManager { * on the system for other users, also install it for the calling user. * @hide */ + // @PrivateApi public abstract int installExistingPackage(String packageName) throws NameNotFoundException; @@ -3114,6 +3153,7 @@ public abstract class PackageManager { * * @hide */ + // @PrivateApi public abstract void deletePackage( String packageName, IPackageDeleteObserver observer, int flags); @@ -3182,6 +3222,7 @@ public abstract class PackageManager { * * @hide */ + // @PrivateApi public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer); /** @@ -3510,6 +3551,9 @@ public abstract class PackageManager { */ public abstract VerifierDeviceIdentity getVerifierDeviceIdentity(); + /** {@hide} */ + public abstract PackageInstaller getPackageInstaller(); + /** * Returns the data directory for a particular user and package, given the uid of the package. * @param uid uid of the package, including the userId and appId diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ff96c51..1c838c3 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -16,6 +16,9 @@ package android.content.pm; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -32,6 +35,7 @@ import android.util.AttributeSet; import android.util.Base64; import android.util.DisplayMetrics; import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.util.TypedValue; @@ -41,6 +45,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.lang.ref.WeakReference; +import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; @@ -51,7 +56,6 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; -import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -61,6 +65,7 @@ import java.util.Set; import java.util.jar.StrictJarFile; import java.util.zip.ZipEntry; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -207,16 +212,20 @@ public class PackageParser { */ public static class PackageLite { public final String packageName; + public final String splitName; public final int versionCode; public final int installLocation; public final VerifierInfo[] verifiers; + public final Signature[] signatures; - public PackageLite(String packageName, int versionCode, - int installLocation, List<VerifierInfo> verifiers) { + public PackageLite(String packageName, String splitName, int versionCode, + int installLocation, List<VerifierInfo> verifiers, Signature[] signatures) { this.packageName = packageName; + this.splitName = splitName; this.versionCode = versionCode; this.installLocation = installLocation; this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]); + this.signatures = signatures; } } @@ -459,7 +468,7 @@ public class PackageParser { return pi; } - private Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je, + private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je, byte[] readBuffer) { try { // We must read the stream for the JarEntry to retrieve @@ -486,6 +495,7 @@ public class PackageParser { public final static int PARSE_ON_SDCARD = 1<<5; public final static int PARSE_IS_SYSTEM_DIR = 1<<6; public final static int PARSE_IS_PRIVILEGED = 1<<7; + public final static int PARSE_GET_SIGNATURES = 1<<8; public int getParseError() { return mParseError; @@ -722,12 +732,8 @@ public class PackageParser { mReadBuffer = readBufferRef; } - if (certs != null && certs.length > 0) { - final int N = certs.length; - pkg.mSignatures = new Signature[certs.length]; - for (int i=0; i<N; i++) { - pkg.mSignatures[i] = new Signature(certs[i]); - } + if (!ArrayUtils.isEmpty(certs)) { + pkg.mSignatures = convertToSignatures(certs); } else { Slog.e(TAG, "Package " + pkg.packageName + " has no certificates; ignoring!"); @@ -762,6 +768,39 @@ public class PackageParser { return true; } + /** + * Only collect certificates on the manifest; does not validate signatures + * across remainder of package. + */ + private static Signature[] collectCertificates(String packageFilePath) { + try { + final StrictJarFile jarFile = new StrictJarFile(packageFilePath); + try { + final ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); + if (jarEntry != null) { + final Certificate[][] certs = loadCertificates(jarFile, jarEntry, null); + return convertToSignatures(certs); + } + } finally { + jarFile.close(); + } + } catch (GeneralSecurityException e) { + Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e); + } catch (IOException e) { + Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e); + } + return null; + } + + private static Signature[] convertToSignatures(Certificate[][] certs) + throws CertificateEncodingException { + final Signature[] res = new Signature[certs.length]; + for (int i = 0; i < certs.length; i++) { + res[i] = new Signature(certs[i]); + } + return res; + } + /* * Utility method that retrieves just the package name and install * location from the apk location at the given file path. @@ -794,11 +833,22 @@ public class PackageParser { return null; } + // Only collect certificates on the manifest; does not validate + // signatures across remainder of package. + final Signature[] signatures; + if ((flags & PARSE_GET_SIGNATURES) != 0) { + signatures = collectCertificates(packageFilePath); + } else { + signatures = null; + } + final AttributeSet attrs = parser; final String errors[] = new String[1]; PackageLite packageLite = null; try { - packageLite = parsePackageLite(res, parser, attrs, flags, errors); + packageLite = parsePackageLite(res, parser, attrs, flags, signatures, errors); + } catch (PackageParserException e) { + Slog.w(TAG, packageFilePath, e); } catch (IOException e) { Slog.w(TAG, packageFilePath, e); } catch (XmlPullParserException e) { @@ -840,72 +890,51 @@ public class PackageParser { ? null : "must have at least one '.' separator"; } - private static String parsePackageName(XmlPullParser parser, - AttributeSet attrs, int flags, String[] outError) - throws IOException, XmlPullParserException { + private static Pair<String, String> parsePackageSplitNames(XmlPullParser parser, + AttributeSet attrs, int flags) throws IOException, XmlPullParserException, + PackageParserException { int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { - ; } if (type != XmlPullParser.START_TAG) { - outError[0] = "No start tag found"; - return null; + throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "No start tag found"); } - if (DEBUG_PARSER) - Slog.v(TAG, "Root element name: '" + parser.getName() + "'"); if (!parser.getName().equals("manifest")) { - outError[0] = "No <manifest> tag"; - return null; + throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "No <manifest> tag"); } - String pkgName = attrs.getAttributeValue(null, "package"); - if (pkgName == null || pkgName.length() == 0) { - outError[0] = "<manifest> does not specify package"; - return null; + + final String packageName = attrs.getAttributeValue(null, "package"); + if (!"android".equals(packageName)) { + final String error = validateName(packageName, true); + if (error != null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + "Invalid manifest package: " + error); + } } - String nameError = validateName(pkgName, true); - if (nameError != null && !"android".equals(pkgName)) { - outError[0] = "<manifest> specifies bad package name \"" - + pkgName + "\": " + nameError; - return null; + + final String splitName = attrs.getAttributeValue(null, "split"); + if (splitName != null) { + final String error = validateName(splitName, true); + if (error != null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + "Invalid manifest split: " + error); + } } - return pkgName.intern(); + return Pair.create(packageName.intern(), + (splitName != null) ? splitName.intern() : splitName); } private static PackageLite parsePackageLite(Resources res, XmlPullParser parser, - AttributeSet attrs, int flags, String[] outError) throws IOException, - XmlPullParserException { - - int type; - while ((type = parser.next()) != XmlPullParser.START_TAG - && type != XmlPullParser.END_DOCUMENT) { - ; - } + AttributeSet attrs, int flags, Signature[] signatures, String[] outError) + throws IOException, XmlPullParserException, PackageParserException { + final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags); - if (type != XmlPullParser.START_TAG) { - outError[0] = "No start tag found"; - return null; - } - if (DEBUG_PARSER) - Slog.v(TAG, "Root element name: '" + parser.getName() + "'"); - if (!parser.getName().equals("manifest")) { - outError[0] = "No <manifest> tag"; - return null; - } - String pkgName = attrs.getAttributeValue(null, "package"); - if (pkgName == null || pkgName.length() == 0) { - outError[0] = "<manifest> does not specify package"; - return null; - } - String nameError = validateName(pkgName, true); - if (nameError != null && !"android".equals(pkgName)) { - outError[0] = "<manifest> specifies bad package name \"" - + pkgName + "\": " + nameError; - return null; - } int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; int versionCode = 0; int numFound = 0; @@ -925,6 +954,7 @@ public class PackageParser { } // Only search the tree when the tag is directly below <manifest> + int type; final int searchDepth = parser.getDepth() + 1; final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>(); @@ -942,7 +972,8 @@ public class PackageParser { } } - return new PackageLite(pkgName.intern(), versionCode, installLocation, verifiers); + return new PackageLite(packageSplit.first, packageSplit.second, versionCode, + installLocation, verifiers, signatures); } /** @@ -966,12 +997,18 @@ public class PackageParser { mParseActivityArgs = null; mParseServiceArgs = null; mParseProviderArgs = null; - - String pkgName = parsePackageName(parser, attrs, flags, outError); - if (pkgName == null) { + + final String pkgName; + final String splitName; + try { + Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags); + pkgName = packageSplit.first; + splitName = packageSplit.second; + } catch (PackageParserException e) { mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; return null; } + int type; if (mOnlyCoreApps) { @@ -982,9 +1019,9 @@ public class PackageParser { } } - final Package pkg = new Package(pkgName); + final Package pkg = new Package(pkgName, splitName); boolean foundApp = false; - + TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifest); pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger( @@ -3544,6 +3581,7 @@ public class PackageParser { public final static class Package { public String packageName; + public String splitName; // For now we only support one application per package. public final ApplicationInfo applicationInfo = new ApplicationInfo(); @@ -3660,9 +3698,10 @@ public class PackageParser { public Set<PublicKey> mSigningKeys; public Map<String, Set<PublicKey>> mKeySetMapping; - public Package(String _name) { - packageName = _name; - applicationInfo.packageName = _name; + public Package(String packageName, String splitName) { + this.packageName = packageName; + this.splitName = splitName; + applicationInfo.packageName = packageName; applicationInfo.uid = -1; } @@ -4267,4 +4306,13 @@ public class PackageParser { public static void setCompatibilityModeEnabled(boolean compatibilityModeEnabled) { sCompatibilityModeEnabled = compatibilityModeEnabled; } + + public static class PackageParserException extends Exception { + public final int error; + + public PackageParserException(int error, String detailMessage) { + super(detailMessage); + this.error = error; + } + } } diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java index f4e7dc3..96aa083 100644 --- a/core/java/android/content/pm/Signature.java +++ b/core/java/android/content/pm/Signature.java @@ -31,8 +31,10 @@ import java.security.cert.CertificateFactory; import java.util.Arrays; /** - * Opaque, immutable representation of a signature associated with an + * Opaque, immutable representation of a signing certificate associated with an * application package. + * <p> + * This class name is slightly misleading, since it's not actually a signature. */ public class Signature implements Parcelable { private final byte[] mSignature; diff --git a/core/java/android/content/pm/VerificationParams.java b/core/java/android/content/pm/VerificationParams.java index 22e1a85..bf1f77f 100644 --- a/core/java/android/content/pm/VerificationParams.java +++ b/core/java/android/content/pm/VerificationParams.java @@ -24,8 +24,10 @@ import android.os.Parcelable; /** * Represents verification parameters used to verify packages to be installed. * + * @deprecated callers should migrate to {@link PackageInstallerParams}. * @hide */ +@Deprecated public class VerificationParams implements Parcelable { /** A constant used to indicate that a uid value is not present. */ public static final int NO_UID = -1; diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 499de17..1692a79 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1475,21 +1475,12 @@ public class Resources { * in length to {@code attrs} or {@code null}. All values * must be of type {@link TypedValue#TYPE_ATTRIBUTE}. * @param attrs The desired attributes to be retrieved. - * @param defStyleAttr An attribute in the current theme that contains a - * reference to a style resource that supplies - * defaults values for the TypedArray. Can be - * 0 to not look for defaults. - * @param defStyleRes A resource identifier of a style resource that - * supplies default values for the TypedArray, - * used only if defStyleAttr is 0 or can not be found - * in the theme. Can be 0 to not look for defaults. * @return Returns a TypedArray holding an array of the attribute * values. Be sure to call {@link TypedArray#recycle()} * when done with it. * @hide */ - public TypedArray resolveAttributes(int[] values, int[] attrs, - int defStyleAttr, int defStyleRes) { + public TypedArray resolveAttributes(int[] values, int[] attrs) { final int len = attrs.length; if (values != null && len != values.length) { throw new IllegalArgumentException( @@ -1497,8 +1488,7 @@ public class Resources { } final TypedArray array = TypedArray.obtain(Resources.this, len); - AssetManager.resolveAttrs(mTheme, defStyleAttr, defStyleRes, - values, attrs, array.mData, array.mIndices); + AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = this; array.mXml = null; diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 15337ce..20dcf83 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -885,13 +885,13 @@ public class TypedArray { /** * Extracts theme attributes from a typed array for later resolution using - * {@link Theme#resolveAttributes(int[], int[], int, int)}. + * {@link Theme#resolveAttributes(int[], int[])}. Removes the entries from + * the typed array so that subsequent calls to typed getters will return the + * default value without crashing. * - * @param array An array to populate with theme attributes. If the array is - * null or not large enough, a new array will be returned. * @return an array of length {@link #getIndexCount()} populated with theme - * attributes, or null if there are no theme attributes in the - * typed array + * attributes, or null if there are no theme attributes in the typed + * array * @hide */ public int[] extractThemeAttrs() { @@ -901,15 +901,27 @@ public class TypedArray { int[] attrs = null; + final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int attrId = getThemeAttributeId(i, 0); - if (attrId != 0) { - if (attrs == null) { - attrs = new int[N]; - } - attrs[i] = attrId; + final int index = i * AssetManager.STYLE_NUM_ENTRIES; + if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + continue; + } + + // Null the entry so that we can safely call getZzz(). + data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; + + final int attr = data[index + AssetManager.STYLE_DATA]; + if (attr == 0) { + // This attribute is useless! + continue; + } + + if (attrs == null) { + attrs = new int[N]; } + attrs[i] = attr; } return attrs; diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 197e3ff..a75372f 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -42,12 +42,8 @@ import android.util.LongSparseArray; public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; - /** The cursor window size. resource xml file specifies the value in kB. - * convert it to bytes here by multiplying with 1024. - */ - private static final int sCursorWindowSize = - Resources.getSystem().getInteger( - com.android.internal.R.integer.config_cursorWindowSize) * 1024; + // This static member will be evaluated when first used. + private static int sCursorWindowSize = -1; /** * The native CursorWindow object pointer. (FOR INTERNAL USE ONLY) @@ -100,6 +96,13 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { public CursorWindow(String name) { mStartPos = 0; mName = name != null && name.length() != 0 ? name : "<unnamed>"; + if (sCursorWindowSize < 0) { + /** The cursor window size. resource xml file specifies the value in kB. + * convert it to bytes here by multiplying with 1024. + */ + sCursorWindowSize = Resources.getSystem().getInteger( + com.android.internal.R.integer.config_cursorWindowSize) * 1024; + } mWindowPtr = nativeCreate(mName, sCursorWindowSize); if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window allocation of " + diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 5f2af8c..b1c1005 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -307,16 +307,14 @@ public final class CameraCharacteristics extends CameraMetadata { * <li>The sizes will be sorted by increasing pixel area (width x height). * If several resolutions have the same area, they will be sorted by increasing width.</li> * <li>The aspect ratio of the largest thumbnail size will be same as the - * aspect ratio of largest JPEG output size in {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations}. + * aspect ratio of largest JPEG output size in android.scaler.availableStreamConfigurations. * The largest size is defined as the size that has the largest pixel area * in a given size list.</li> - * <li>Each output JPEG size in {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations} will have at least + * <li>Each output JPEG size in android.scaler.availableStreamConfigurations will have at least * one corresponding size that has the same aspect ratio in availableThumbnailSizes, * and vice versa.</li> * <li>All non (0, 0) sizes will have non-zero widths and heights.</li> * </ul> - * - * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS */ public static final Key<android.hardware.camera2.Size[]> JPEG_AVAILABLE_THUMBNAIL_SIZES = new Key<android.hardware.camera2.Size[]>("android.jpeg.availableThumbnailSizes", android.hardware.camera2.Size[].class); @@ -445,8 +443,10 @@ public final class CameraCharacteristics extends CameraMetadata { * working at that point; DO NOT USE without careful * consideration of future support.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * @deprecated * @hide */ + @Deprecated public static final Key<Byte> QUIRKS_USE_PARTIAL_RESULT = new Key<Byte>("android.quirks.usePartialResult", byte.class); @@ -461,8 +461,8 @@ public final class CameraCharacteristics extends CameraMetadata { * <p>This lists the upper bound of the number of output streams supported by * the camera device. Using more streams simultaneously may require more hardware and * CPU resources that will consume more power. The image format for a output stream can - * be any supported format provided by {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations}. - * The formats defined in {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations} can be catergorized + * be any supported format provided by android.scaler.availableStreamConfigurations. + * The formats defined in android.scaler.availableStreamConfigurations can be catergorized * into the 3 stream types as below:</p> * <ul> * <li>Processed (but stalling): any non-RAW format with a stallDurations > 0. @@ -471,8 +471,6 @@ public final class CameraCharacteristics extends CameraMetadata { * <li>Processed (but not-stalling): any non-RAW format without a stall duration. * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li> * </ul> - * - * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS */ public static final Key<int[]> REQUEST_MAX_NUM_OUTPUT_STREAMS = new Key<int[]>("android.request.maxNumOutputStreams", int[].class); @@ -483,14 +481,12 @@ public final class CameraCharacteristics extends CameraMetadata { * <p>When set to 0, it means no input stream is supported.</p> * <p>The image format for a input stream can be any supported * format provided by - * {@link CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP android.scaler.availableInputOutputFormatsMap}. When using an + * android.scaler.availableInputOutputFormatsMap. When using an * input stream, there must be at least one output stream * configured to to receive the reprocessed images.</p> * <p>For example, for Zero Shutter Lag (ZSL) still capture use case, the input * stream image format will be RAW_OPAQUE, the associated output stream image format * should be JPEG.</p> - * - * @see CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP */ public static final Key<Integer> REQUEST_MAX_NUM_INPUT_STREAMS = new Key<Integer>("android.request.maxNumInputStreams", int.class); @@ -629,22 +625,26 @@ public final class CameraCharacteristics extends CameraMetadata { * camera device for output streams.</p> * <p>All camera devices will support JPEG and YUV_420_888 formats.</p> * <p>When set to YUV_420_888, application can access the YUV420 data directly.</p> + * @deprecated + * @hide */ + @Deprecated public static final Key<int[]> SCALER_AVAILABLE_FORMATS = new Key<int[]>("android.scaler.availableFormats", int[].class); /** * <p>The minimum frame duration that is supported - * for each resolution in {@link CameraCharacteristics#SCALER_AVAILABLE_JPEG_SIZES android.scaler.availableJpegSizes}.</p> + * for each resolution in android.scaler.availableJpegSizes.</p> * <p>This corresponds to the minimum steady-state frame duration when only * that JPEG stream is active and captured in a burst, with all * processing (typically in android.*.mode) set to FAST.</p> * <p>When multiple streams are configured, the minimum * frame duration will be >= max(individual stream min * durations)</p> - * - * @see CameraCharacteristics#SCALER_AVAILABLE_JPEG_SIZES + * @deprecated + * @hide */ + @Deprecated public static final Key<long[]> SCALER_AVAILABLE_JPEG_MIN_DURATIONS = new Key<long[]>("android.scaler.availableJpegMinDurations", long[].class); @@ -654,7 +654,10 @@ public final class CameraCharacteristics extends CameraMetadata { * sensor maximum resolution (defined by {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}).</p> * * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @deprecated + * @hide */ + @Deprecated public static final Key<android.hardware.camera2.Size[]> SCALER_AVAILABLE_JPEG_SIZES = new Key<android.hardware.camera2.Size[]>("android.scaler.availableJpegSizes", android.hardware.camera2.Size[].class); @@ -669,16 +672,17 @@ public final class CameraCharacteristics extends CameraMetadata { /** * <p>For each available processed output size (defined in - * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES android.scaler.availableProcessedSizes}), this property lists the + * android.scaler.availableProcessedSizes), this property lists the * minimum supportable frame duration for that size.</p> * <p>This should correspond to the frame duration when only that processed * stream is active, with all processing (typically in android.*.mode) * set to FAST.</p> * <p>When multiple streams are configured, the minimum frame duration will * be >= max(individual stream min durations).</p> - * - * @see CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES + * @deprecated + * @hide */ + @Deprecated public static final Key<long[]> SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS = new Key<long[]>("android.scaler.availableProcessedMinDurations", long[].class); @@ -696,7 +700,10 @@ public final class CameraCharacteristics extends CameraMetadata { * can provide.</p> * <p>Please reference the documentation for the image data destination to * check if it limits the maximum size for image data.</p> + * @deprecated + * @hide */ + @Deprecated public static final Key<android.hardware.camera2.Size[]> SCALER_AVAILABLE_PROCESSED_SIZES = new Key<android.hardware.camera2.Size[]>("android.scaler.availableProcessedSizes", android.hardware.camera2.Size[].class); @@ -746,13 +753,14 @@ public final class CameraCharacteristics extends CameraMetadata { * </table> * <p>For ZSL-capable camera devices, using the RAW_OPAQUE format * as either input or output will never hurt maximum frame rate (i.e. - * {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS android.scaler.availableStallDurations} will not have RAW_OPAQUE).</p> + * StreamConfigurationMap#getOutputStallDuration(int,Size) + * for a <code>format =</code> RAW_OPAQUE is always 0).</p> * <p>Attempting to configure an input stream with output streams not * listed as available in this map is not valid.</p> - * <p>TODO: Add java type mapping for this property.</p> + * <p>TODO: typedef to ReprocessFormatMap</p> * * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS - * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS + * @hide */ public static final Key<int[]> SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP = new Key<int[]>("android.scaler.availableInputOutputFormatsMap", int[].class); @@ -775,7 +783,7 @@ public final class CameraCharacteristics extends CameraMetadata { * check if it limits the maximum size for image data.</p> * <p>Not all output formats may be supported in a configuration with * an input stream of a particular format. For more details, see - * {@link CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP android.scaler.availableInputOutputFormatsMap}.</p> + * android.scaler.availableInputOutputFormatsMap.</p> * <p>The following table describes the minimum required output stream * configurations based on the hardware level * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}):</p> @@ -844,13 +852,11 @@ public final class CameraCharacteristics extends CameraMetadata { * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES - * @see CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE - * @see #SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT - * @see #SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT + * @hide */ - public static final Key<int[]> SCALER_AVAILABLE_STREAM_CONFIGURATIONS = - new Key<int[]>("android.scaler.availableStreamConfigurations", int[].class); + public static final Key<android.hardware.camera2.params.StreamConfiguration[]> SCALER_AVAILABLE_STREAM_CONFIGURATIONS = + new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.scaler.availableStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class); /** * <p>This lists the minimum frame duration for each @@ -863,14 +869,16 @@ public final class CameraCharacteristics extends CameraMetadata { * <p>The minimum frame duration of a stream (of a particular format, size) * is the same regardless of whether the stream is input or output.</p> * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and - * {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS android.scaler.availableStallDurations} for more details about + * android.scaler.availableStallDurations for more details about * calculating the max frame rate.</p> + * <p>(Keep in sync with + * StreamConfigurationMap#getOutputMinFrameDuration)</p> * - * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS * @see CaptureRequest#SENSOR_FRAME_DURATION + * @hide */ - public static final Key<long[]> SCALER_AVAILABLE_MIN_FRAME_DURATIONS = - new Key<long[]>("android.scaler.availableMinFrameDurations", long[].class); + public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> SCALER_AVAILABLE_MIN_FRAME_DURATIONS = + new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.scaler.availableMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class); /** * <p>This lists the maximum stall duration for each @@ -929,12 +937,105 @@ public final class CameraCharacteristics extends CameraMetadata { * for more details.</p> * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} for more information about * calculating the max frame rate (absent stalls).</p> + * <p>(Keep up to date with + * StreamConfigurationMap#getOutputStallDuration(int, Size) )</p> * * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES * @see CaptureRequest#SENSOR_FRAME_DURATION + * @hide */ - public static final Key<long[]> SCALER_AVAILABLE_STALL_DURATIONS = - new Key<long[]>("android.scaler.availableStallDurations", long[].class); + public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> SCALER_AVAILABLE_STALL_DURATIONS = + new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.scaler.availableStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class); + + /** + * <p>The available stream configurations that this + * camera device supports; also includes the minimum frame durations + * and the stall durations for each format/size combination.</p> + * <p>All camera devices will support sensor maximum resolution (defined by + * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}) for the JPEG format.</p> + * <p>For a given use case, the actual maximum supported resolution + * may be lower than what is listed here, depending on the destination + * Surface for the image data. For example, for recording video, + * the video encoder chosen may have a maximum size limit (e.g. 1080p) + * smaller than what the camera (e.g. maximum resolution is 3264x2448) + * can provide.</p> + * <p>Please reference the documentation for the image data destination to + * check if it limits the maximum size for image data.</p> + * <p>Not all output formats may be supported in a configuration with + * an input stream of a particular format. For more details, see + * android.scaler.availableInputOutputFormatsMap.</p> + * <p>The following table describes the minimum required output stream + * configurations based on the hardware level + * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}):</p> + * <table> + * <thead> + * <tr> + * <th align="center">Format</th> + * <th align="center">Size</th> + * <th align="center">Hardware Level</th> + * <th align="center">Notes</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td align="center">JPEG</td> + * <td align="center">{@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</td> + * <td align="center">Any</td> + * <td align="center"></td> + * </tr> + * <tr> + * <td align="center">JPEG</td> + * <td align="center">1920x1080 (1080p)</td> + * <td align="center">Any</td> + * <td align="center">if 1080p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">JPEG</td> + * <td align="center">1280x720 (720)</td> + * <td align="center">Any</td> + * <td align="center">if 720p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">JPEG</td> + * <td align="center">640x480 (480p)</td> + * <td align="center">Any</td> + * <td align="center">if 480p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">JPEG</td> + * <td align="center">320x240 (240p)</td> + * <td align="center">Any</td> + * <td align="center">if 240p <= activeArraySize</td> + * </tr> + * <tr> + * <td align="center">YUV_420_888</td> + * <td align="center">all output sizes available for JPEG</td> + * <td align="center">FULL</td> + * <td align="center"></td> + * </tr> + * <tr> + * <td align="center">YUV_420_888</td> + * <td align="center">all output sizes available for JPEG, up to the maximum video size</td> + * <td align="center">LIMITED</td> + * <td align="center"></td> + * </tr> + * <tr> + * <td align="center">IMPLEMENTATION_DEFINED</td> + * <td align="center">same as YUV_420_888</td> + * <td align="center">Any</td> + * <td align="center"></td> + * </tr> + * </tbody> + * </table> + * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional + * mandatory stream configurations on a per-capability basis.</p> + * + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE + */ + public static final Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP = + new Key<android.hardware.camera2.params.StreamConfigurationMap>("android.scaler.streamConfigurationMap", android.hardware.camera2.params.StreamConfigurationMap.class); /** * <p>Area of raw data which corresponds to only @@ -982,13 +1083,9 @@ public final class CameraCharacteristics extends CameraMetadata { * being clipped to the maximum. See that control * for a full definition of frame durations.</p> * <p>Refer to - * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS android.scaler.availableProcessedMinDurations}, - * {@link CameraCharacteristics#SCALER_AVAILABLE_JPEG_MIN_DURATIONS android.scaler.availableJpegMinDurations}, and - * android.scaler.availableRawMinDurations for the minimum - * frame duration values.</p> + * StreamConfigurationMap#getOutputMinFrameDuration(int,Size) + * for the minimum frame duration values.</p> * - * @see CameraCharacteristics#SCALER_AVAILABLE_JPEG_MIN_DURATIONS - * @see CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS * @see CaptureRequest#SENSOR_FRAME_DURATION */ public static final Key<Long> SENSOR_INFO_MAX_FRAME_DURATION = @@ -1007,9 +1104,7 @@ public final class CameraCharacteristics extends CameraMetadata { * including black calibration pixels.</p> * <p>Maximum output resolution for raw format must * match this in - * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS android.scaler.availableStreamConfigurations}.</p> - * - * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS + * android.scaler.availableStreamConfigurations.</p> */ public static final Key<android.hardware.camera2.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE = new Key<android.hardware.camera2.Size>("android.sensor.info.pixelArraySize", android.hardware.camera2.Size.class); @@ -1420,4 +1515,13 @@ public final class CameraCharacteristics extends CameraMetadata { /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ + + + + + + + + + } diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 9d0e0e1..ca03dae 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -16,6 +16,8 @@ package android.hardware.camera2; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.graphics.ImageFormat; import android.os.Handler; import android.view.Surface; @@ -147,7 +149,7 @@ public interface CameraDevice extends AutoCloseable { * the size of the Surface with * {@link android.view.SurfaceHolder#setFixedSize} to be one of the * supported - * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} + * {@link StreamConfigurationMap#getOutputSizes(Class) processed sizes} * before calling {@link android.view.SurfaceHolder#getSurface}.</li> * * <li>For accessing through an OpenGL texture via a @@ -155,14 +157,14 @@ public interface CameraDevice extends AutoCloseable { * the SurfaceTexture with * {@link android.graphics.SurfaceTexture#setDefaultBufferSize} to be one * of the supported - * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} + * {@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 CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} + * {@link StreamConfigurationMap#getOutputSizes(Class) processed sizes} * </li> * * <li>For recording with {@link android.media.MediaRecorder}: TODO</li> @@ -171,18 +173,15 @@ public interface CameraDevice extends AutoCloseable { * Create a RenderScript * {@link android.renderscript.Allocation Allocation} with a supported YUV * type, the IO_INPUT flag, and one of the supported - * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes}. Then + * {@link StreamConfigurationMap#getOutputSizes(int) processed sizes}. Then * obtain the Surface with * {@link android.renderscript.Allocation#getSurface}.</li> * - * <li>For access to uncompressed or JPEG data in the application: Create a - * {@link android.media.ImageReader} object with the desired - * {@link CameraCharacteristics#SCALER_AVAILABLE_FORMATS image format}, and a - * size from the matching - * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed}, - * {@link CameraCharacteristics#SCALER_AVAILABLE_JPEG_SIZES jpeg}. Then obtain - * a Surface from it.</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> diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 6659278..a11390d 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -446,20 +446,6 @@ public abstract class CameraMetadata { public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5; // - // Enumeration values for CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS - // - - /** - * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS - */ - public static final int SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT = 0; - - /** - * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS - */ - public static final int SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT = 1; - - // // Enumeration values for CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT // diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 0ca9161..8ae21f3 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1185,7 +1185,8 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * cannot process more than 1 capture at a time.</li> * </ul> * <p>The necessary information for the application, given the model above, - * is provided via the {@link CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS android.scaler.availableMinFrameDurations} field. + * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field + * using StreamConfigurationMap#getOutputMinFrameDuration(int, Size). * These are used to determine the maximum frame rate / minimum frame * duration that is possible for a given stream configuration.</p> * <p>Specifically, the application can use the following rules to @@ -1195,7 +1196,8 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * <li>Let the set of currently configured input/output streams * be called <code>S</code>.</li> * <li>Find the minimum frame durations for each stream in <code>S</code>, by - * looking it up in {@link CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS android.scaler.availableMinFrameDurations} (with + * looking it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using + * StreamConfigurationMap#getOutputMinFrameDuration(int, Size) (with * its respective size/format). Let this set of frame durations be called * <code>F</code>.</li> * <li>For any given request <code>R</code>, the minimum frame duration allowed @@ -1203,7 +1205,8 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * used in <code>R</code> be called <code>S_r</code>.</li> * </ol> * <p>If none of the streams in <code>S_r</code> have a stall time (listed in - * {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS android.scaler.availableStallDurations}), then the frame duration in + * StreamConfigurationMap#getOutputStallDuration(int,Size) using its + * respective size/format), then the frame duration in * <code>F</code> determines the steady state frame rate that the application will * get if it uses <code>R</code> as a repeating request. Let this special kind * of request be called <code>Rsimple</code>.</p> @@ -1214,10 +1217,9 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { * if all buffers from the previous <code>Rstall</code> have already been * delivered.</p> * <p>For more details about stalling, see - * {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS android.scaler.availableStallDurations}.</p> + * StreamConfigurationMap#getOutputStallDuration(int,Size).</p> * - * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS - * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS + * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP */ public static final Key<Long> SENSOR_FRAME_DURATION = new Key<Long>("android.sensor.frameDuration", long.class); @@ -1516,4 +1518,12 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ + + + + + + + + } diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 42a3de8..0160622 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -216,18 +216,6 @@ public final class CaptureResult extends CameraMetadata { new Key<float[]>("android.colorCorrection.gains", float[].class); /** - * <p>The ID sent with the latest - * CAMERA2_TRIGGER_PRECAPTURE_METERING call</p> - * <p>Must be 0 if no - * CAMERA2_TRIGGER_PRECAPTURE_METERING trigger received yet - * by HAL. Always updated even if AE algorithm ignores the - * trigger</p> - * @hide - */ - public static final Key<Integer> CONTROL_AE_PRECAPTURE_ID = - new Key<Integer>("android.control.aePrecaptureId", int.class); - - /** * <p>The desired setting for the camera device's auto-exposure * algorithm's antibanding compensation.</p> * <p>Some kinds of lighting fixtures, such as some fluorescent @@ -1068,17 +1056,6 @@ public final class CaptureResult extends CameraMetadata { new Key<Integer>("android.control.afState", int.class); /** - * <p>The ID sent with the latest - * CAMERA2_TRIGGER_AUTOFOCUS call</p> - * <p>Must be 0 if no CAMERA2_TRIGGER_AUTOFOCUS trigger - * received yet by HAL. Always updated even if AF algorithm - * ignores the trigger</p> - * @hide - */ - public static final Key<Integer> CONTROL_AF_TRIGGER_ID = - new Key<Integer>("android.control.afTriggerId", int.class); - - /** * <p>Whether AWB is currently locked to its * latest calculated values.</p> * <p>Note that AWB lock is only meaningful for AUTO @@ -1713,8 +1690,10 @@ public final class CaptureResult extends CameraMetadata { * capture must arrive before the FINAL buffer for that capture. This entry may * only be used by the camera device if quirks.usePartialResult is set to 1.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * @deprecated * @hide */ + @Deprecated public static final Key<Boolean> QUIRKS_PARTIAL_RESULT = new Key<Boolean>("android.quirks.partialResult", boolean.class); @@ -1834,7 +1813,8 @@ public final class CaptureResult extends CameraMetadata { * cannot process more than 1 capture at a time.</li> * </ul> * <p>The necessary information for the application, given the model above, - * is provided via the {@link CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS android.scaler.availableMinFrameDurations} field. + * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field + * using StreamConfigurationMap#getOutputMinFrameDuration(int, Size). * These are used to determine the maximum frame rate / minimum frame * duration that is possible for a given stream configuration.</p> * <p>Specifically, the application can use the following rules to @@ -1844,7 +1824,8 @@ public final class CaptureResult extends CameraMetadata { * <li>Let the set of currently configured input/output streams * be called <code>S</code>.</li> * <li>Find the minimum frame durations for each stream in <code>S</code>, by - * looking it up in {@link CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS android.scaler.availableMinFrameDurations} (with + * looking it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using + * StreamConfigurationMap#getOutputMinFrameDuration(int, Size) (with * its respective size/format). Let this set of frame durations be called * <code>F</code>.</li> * <li>For any given request <code>R</code>, the minimum frame duration allowed @@ -1852,7 +1833,8 @@ public final class CaptureResult extends CameraMetadata { * used in <code>R</code> be called <code>S_r</code>.</li> * </ol> * <p>If none of the streams in <code>S_r</code> have a stall time (listed in - * {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS android.scaler.availableStallDurations}), then the frame duration in + * StreamConfigurationMap#getOutputStallDuration(int,Size) using its + * respective size/format), then the frame duration in * <code>F</code> determines the steady state frame rate that the application will * get if it uses <code>R</code> as a repeating request. Let this special kind * of request be called <code>Rsimple</code>.</p> @@ -1863,10 +1845,9 @@ public final class CaptureResult extends CameraMetadata { * if all buffers from the previous <code>Rstall</code> have already been * delivered.</p> * <p>For more details about stalling, see - * {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS android.scaler.availableStallDurations}.</p> + * StreamConfigurationMap#getOutputStallDuration(int,Size).</p> * - * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS - * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS + * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP */ public static final Key<Long> SENSOR_FRAME_DURATION = new Key<Long>("android.sensor.frameDuration", long.class); @@ -2141,8 +2122,10 @@ public final class CaptureResult extends CameraMetadata { * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> * * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @deprecated * @hide */ + @Deprecated public static final Key<float[]> STATISTICS_PREDICTED_COLOR_GAINS = new Key<float[]>("android.statistics.predictedColorGains", float[].class); @@ -2163,8 +2146,10 @@ public final class CaptureResult extends CameraMetadata { * <p>This value should always be calculated by the 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 * @hide */ + @Deprecated public static final Key<Rational[]> STATISTICS_PREDICTED_COLOR_TRANSFORM = new Key<Rational[]>("android.statistics.predictedColorTransform", Rational[].class); @@ -2441,6 +2426,14 @@ public final class CaptureResult extends CameraMetadata { * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ + + + + + + + + /** * <p> * List of the {@link Face Faces} detected through camera face detection diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl index d77f3d1..0815170 100644 --- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl +++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl @@ -20,7 +20,7 @@ import android.view.Surface; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.CaptureRequest; -import android.hardware.camera2.LongParcelable; +import android.hardware.camera2.utils.LongParcelable; /** @hide */ interface ICameraDeviceUser diff --git a/core/java/android/hardware/camera2/StreamConfigurationMap.java b/core/java/android/hardware/camera2/StreamConfigurationMap.java deleted file mode 100644 index 5ddd7d6..0000000 --- a/core/java/android/hardware/camera2/StreamConfigurationMap.java +++ /dev/null @@ -1,508 +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.hardware.camera2; - -import android.graphics.ImageFormat; -import android.graphics.PixelFormat; -import android.hardware.camera2.utils.HashCodeHelpers; -import android.view.Surface; -import android.util.Size; - -import java.util.Arrays; - -import static com.android.internal.util.Preconditions.*; - -/** - * Immutable class to store the available stream - * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to be used - * when configuring streams with {@link CameraDevice#configureOutputs}. - * <!-- TODO: link to input stream configuration --> - * - * <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively - * for that format) that are supported by a camera device.</p> - * - * <p>This also contains the minimum frame durations and stall durations for each format/size - * combination that can be used to calculate effective frame rate when submitting multiple captures. - * </p> - * - * <p>An instance of this object is available from {@link CameraCharacteristics} using - * the {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS} key and the - * {@link CameraCharacteristics#get} method.</p. - * - * <pre>{@code - * CameraCharacteristics characteristics = ...; - * StreamConfigurationMap configs = characteristics.get( - * CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS); - * }</pre> - * - * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS - * @see CameraDevice#configureOutputs - */ -public final class StreamConfigurationMap { - - /** - * Create a new {@link StreamConfigurationMap}. - * - * <p>The array parameters ownership is passed to this object after creation; do not - * write to them after this constructor is invoked.</p> - * - * @param configurations a non-{@code null} array of {@link StreamConfiguration} - * @param durations a non-{@code null} array of {@link StreamConfigurationDuration} - * - * @throws NullPointerException if any of the arguments or subelements were {@code null} - * - * @hide - */ - public StreamConfigurationMap( - StreamConfiguration[] configurations, - StreamConfigurationDuration[] durations) { - // TODO: format check against ImageFormat/PixelFormat ? - - mConfigurations = checkArrayElementsNotNull(configurations, "configurations"); - mDurations = checkArrayElementsNotNull(durations, "durations"); - - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get the image {@code format} output formats in this stream configuration. - * - * <p>All image formats returned by this function will be defined in either {@link ImageFormat} - * or in {@link PixelFormat} (and there is no possibility of collision).</p> - * - * <p>Formats listed in this array are guaranteed to return true if queried with - * {@link #isOutputSupportedFor(int).</p> - * - * @return an array of integer format - * - * @see ImageFormat - * @see PixelFormat - */ - public final int[] getOutputFormats() { - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get the image {@code format} input formats in this stream configuration. - * - * <p>All image formats returned by this function will be defined in either {@link ImageFormat} - * or in {@link PixelFormat} (and there is no possibility of collision).</p> - * - * @return an array of integer format - * - * @see ImageFormat - * @see PixelFormat - * - * @hide - */ - public final int[] getInputFormats() { - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get the supported input sizes for this input format. - * - * <p>The format must have come from {@link #getInputFormats}; otherwise - * {@code null} is returned.</p> - * - * @param format a format from {@link #getInputFormats} - * @return a non-empty array of sizes, or {@code null} if the format was not available. - * - * @hide - */ - public Size[] getInputSizes(final int format) { - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Determine whether or not output streams can be - * {@link CameraDevice#configureOutputs configured} with a particular user-defined format. - * - * <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}. - * Refer to the class which provides the surface for additional documentation.</p> - * - * <p>Formats for which this returns {@code true} are guaranteed to exist in the result - * returned by {@link #getOutputSizes}.</p> - * - * @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} - * - * @throws IllegalArgumentException - * if the image format was not a defined named constant - * from either {@link ImageFormat} or {@link PixelFormat} - * - * @see ImageFormat - * @see PixelFormat - * @see CameraDevice#configureOutputs - */ - public boolean isOutputSupportedFor(int format) { - checkArgumentFormat(format); - - final int[] formats = getOutputFormats(); - for (int i = 0; i < formats.length; ++i) { - if (format == formats[i]) { - return true; - } - } - - return false; - } - - /** - * Determine whether or not output streams can be configured with a particular class - * as a consumer. - * - * <p>The following list is generally usable for outputs: - * <ul> - * <li>{@link android.media.ImageReader} - - * Recommended for image processing or streaming to external resources (such as a file or - * network) - * <li>{@link android.media.MediaRecorder} - - * Recommended for recording video (simple to use) - * <li>{@link android.media.MediaCodec} - - * Recommended for recording video (more complicated to use, with more flexibility) - * <li>{@link android.renderscript.Allocation} - - * Recommended for image processing with {@link android.renderscript RenderScript} - * <li>{@link android.view.SurfaceHolder} - - * Recommended for low-power camera preview with {@link android.view.SurfaceView} - * <li>{@link android.graphics.SurfaceTexture} - - * Recommended for OpenGL-accelerated preview processing or compositing with - * {@link android.view.TextureView} - * </ul> - * </p> - * - * <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> - * - * <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> - * - * @param klass a non-{@code null} {@link Class} object reference - * @return {@code true} if this class is supported as an output, {@code false} otherwise - * - * @throws NullPointerException if {@code klass} was {@code null} - * - * @see CameraDevice#configureOutputs - * @see #isOutputSupportedFor(Surface) - */ - public static <T> boolean isOutputSupportedFor(final Class<T> klass) { - checkNotNull(klass, "klass must not be null"); - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Determine whether or not the {@code surface} in its current state is suitable to be - * {@link CameraDevice#configureOutputs configured} 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 - * compatible with the {@link CameraDevice} in general - * (see {@link #isOutputSupportedFor(Class)}, but it is the caller's responsibility to put the - * {@code surface} into a state that will be compatible with the {@link CameraDevice}.</p> - * - * <p>Reasons for a {@code surface} being specifically incompatible might be: - * <ul> - * <li>Using a format that's not listed by {@link #getOutputFormats} - * <li>Using a format/size combination that's not listed by {@link #getOutputSizes} - * <li>The {@code surface} itself is not in a state where it can service a new producer.</p> - * </li> - * </ul> - * - * This is not an exhaustive list; see the particular class's documentation for further - * possible reasons of incompatibility.</p> - * - * @param surface a non-{@code null} {@link Surface} object reference - * @return {@code true} if this is supported, {@code false} otherwise - * - * @throws NullPointerException if {@code surface} was {@code null} - * - * @see CameraDevice#configureOutputs - * @see #isOutputSupportedFor(Class) - */ - public boolean isOutputSupportedFor(final Surface surface) { - checkNotNull(surface, "surface must not be null"); - - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get a list of sizes compatible with {@code klass} to use as an output. - * - * <p>Since some of the supported classes may support additional formats beyond - * an opaque/implementation-defined (under-the-hood) format; this function only returns - * sizes for the implementation-defined format.</p> - * - * <p>Some classes such as {@link android.media.ImageReader} may only support user-defined - * formats; in particular {@link #isOutputSupportedFor(Class)} will return {@code true} for - * that class and this method will return an empty array (but not {@code null}).</p> - * - * <p>If a well-defined format such as {@code NV21} is required, use - * {@link #getOutputSizes(int)} instead.</p> - * - * <p>The {@code klass} should be a supported output, that querying - * {@code #isOutputSupportedFor(Class)} should return {@code true}.</p> - * - * @param klass - * a non-{@code null} {@link Class} object reference - * @return - * an array of supported sizes for implementation-defined formats, - * or {@code null} iff the {@code klass} is not a supported output - * - * @throws NullPointerException if {@code klass} was {@code null} - * - * @see #isOutputSupportedFor(Class) - */ - public <T> Size[] getOutputSizes(final Class<T> klass) { - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get a list of sizes compatible with the requested image {@code format}. - * - * <p>The {@code format} should be a supported format (one of the formats returned by - * {@link #getOutputFormats}).</p> - * - * @param format an image format from {@link ImageFormat} or {@link PixelFormat} - * @return - * an array of supported sizes, - * or {@code null} if the {@code format} is not a supported output - * - * @see ImageFormat - * @see PixelFormat - * @see #getOutputFormats - */ - public Size[] getOutputSizes(final int format) { - try { - checkArgumentFormatSupported(format, /*output*/true); - } catch (IllegalArgumentException e) { - return null; - } - - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration} - * for the format/size combination (in nanoseconds). - * - * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p> - * <p>{@code size} should be one of the ones returned by - * {@link #getOutputSizes(int)}.</p> - * - * @param format an image format from {@link ImageFormat} or {@link PixelFormat} - * @param size an output-compatible size - * @return a minimum frame duration {@code >=} 0 in nanoseconds - * - * @throws IllegalArgumentException if {@code format} or {@code size} was not supported - * @throws NullPointerException if {@code size} was {@code null} - * - * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS - * @see CaptureRequest#SENSOR_FRAME_DURATION - * @see ImageFormat - * @see PixelFormat - */ - public long getOutputMinFrameDuration(final int format, final Size size) { - checkArgumentFormatSupported(format, /*output*/true); - - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration} - * for the class/size combination (in nanoseconds). - * - * <p>This assumes a the {@code klass} is set up to use an implementation-defined format. - * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p> - * - * <p>{@code klass} should be one of the ones which is supported by - * {@link #isOutputSupportedFor(Class)}.</p> - * - * <p>{@code size} should be one of the ones returned by - * {@link #getOutputSizes(int)}.</p> - * - * @param klass - * a class which is supported by {@link #isOutputSupportedFor(Class)} and has a - * non-empty array returned by {@link #getOutputSizes(Class)} - * @param size an output-compatible size - * @return a minimum frame duration {@code >=} 0 in nanoseconds - * - * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported - * @throws NullPointerException if {@code size} or {@code klass} was {@code null} - * - * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS - * @see CaptureRequest#SENSOR_FRAME_DURATION - * @see ImageFormat - * @see PixelFormat - */ - public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) { - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get the {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS stall duration} - * for the format/size combination (in nanoseconds). - * - * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p> - * <p>{@code size} should be one of the ones returned by - * {@link #getOutputSizes(int)}.</p> - * - * @param format an image format from {@link ImageFormat} or {@link PixelFormat} - * @param size an output-compatible size - * @return a stall duration {@code >=} 0 in nanoseconds - * - * @throws IllegalArgumentException if {@code format} or {@code size} was not supported - * @throws NullPointerException if {@code size} was {@code null} - * - * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS - * @see ImageFormat - * @see PixelFormat - */ - public long getOutputStallDuration(final int format, final Size size) { - checkArgumentFormatSupported(format, /*output*/true); - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Get the {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS stall duration} - * for the class/size combination (in nanoseconds). - * - * <p>This assumes a the {@code klass} is set up to use an implementation-defined format. - * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p> - * - * <p>{@code klass} should be one of the ones with a non-empty array returned by - * {@link #getOutputSizes(Class)}.</p> - * - * <p>{@code size} should be one of the ones returned by - * {@link #getOutputSizes(Class)}.</p> - * - * @param klass - * a class which is supported by {@link #isOutputSupportedFor(Class)} and has a - * non-empty array returned by {@link #getOutputSizes(Class)} - * @param size an output-compatible size - * @return a minimum frame duration {@code >=} 0 in nanoseconds - * - * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported - * @throws NullPointerException if {@code size} or {@code klass} was {@code null} - * - * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS - * @see CaptureRequest#SENSOR_FRAME_DURATION - * @see ImageFormat - * @see PixelFormat - */ - public <T> long getOutputStallDuration(final Class<T> klass, final Size size) { - throw new UnsupportedOperationException("Not implemented yet"); - } - - /** - * Check if this {@link StreamConfigurationMap} is equal to another - * {@link StreamConfigurationMap}. - * - * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p> - * - * @return {@code true} if the objects were equal, {@code false} otherwise - */ - @Override - public boolean equals(final Object obj) { - if (obj == null) { - return false; - } - if (this == obj) { - return true; - } - if (obj instanceof StreamConfigurationMap) { - final StreamConfigurationMap other = (StreamConfigurationMap) obj; - // TODO: do we care about order? - return Arrays.equals(mConfigurations, other.mConfigurations) && - Arrays.equals(mDurations, other.mDurations); - } - return false; - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - // TODO: do we care about order? - return HashCodeHelpers.hashCode(mConfigurations) ^ HashCodeHelpers.hashCode(mDurations); - } - - // Check that the argument is supported by #getOutputFormats or #getInputFormats - private int checkArgumentFormatSupported(int format, boolean output) { - checkArgumentFormat(format); - - int[] formats = output ? getOutputFormats() : getInputFormats(); - for (int i = 0; i < formats.length; ++i) { - if (format == formats[i]) { - return format; - } - } - - throw new IllegalArgumentException(String.format( - "format %x is not supported by this stream configuration map", format)); - } - - /** - * Ensures that the format is either user-defined or implementation defined. - * - * <p>Any invalid/undefined formats will raise an exception.</p> - * - * @param format image format - * @return the format - * - * @throws IllegalArgumentException if the format was invalid - */ - static int checkArgumentFormatInternal(int format) { - if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { - return format; - } - - return checkArgumentFormat(format); - } - - /** - * Ensures that the format is user-defined in either ImageFormat or PixelFormat. - * - * <p>Any invalid/undefined formats will raise an exception, including implementation-defined. - * </p> - * - * <p>Note that {@code @hide} and deprecated formats will not pass this check.</p> - * - * @param format image format - * @return the format - * - * @throws IllegalArgumentException if the format was not user-defined - */ - static int checkArgumentFormat(int format) { - if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { - throw new IllegalArgumentException(String.format( - "format %x was not defined in either ImageFormat or PixelFormat", format)); - } - - return format; - } - - private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22; - - private final StreamConfiguration[] mConfigurations; - private final StreamConfigurationDuration[] mDurations; - -} diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java index 988f8f9..628d1c3 100644 --- a/core/java/android/hardware/camera2/impl/CameraDevice.java +++ b/core/java/android/hardware/camera2/impl/CameraDevice.java @@ -24,9 +24,9 @@ import android.hardware.camera2.CaptureResult; import android.hardware.camera2.CaptureResultExtras; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; -import android.hardware.camera2.LongParcelable; import android.hardware.camera2.utils.CameraBinderDecorator; import android.hardware.camera2.utils.CameraRuntimeException; +import android.hardware.camera2.utils.LongParcelable; import android.os.Handler; import android.os.IBinder; import android.os.Looper; diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 9a06e97..d28f7bd 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -43,6 +43,9 @@ import android.hardware.camera2.marshal.impl.MarshalQueryableSizeF; import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfiguration; import android.hardware.camera2.marshal.impl.MarshalQueryableStreamConfigurationDuration; import android.hardware.camera2.marshal.impl.MarshalQueryableString; +import android.hardware.camera2.params.StreamConfiguration; +import android.hardware.camera2.params.StreamConfigurationDuration; +import android.hardware.camera2.params.StreamConfigurationMap; import android.os.Parcelable; import android.os.Parcel; import android.util.Log; @@ -207,10 +210,8 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return (T) getFaces(); } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) { return (T) getFaceRectangles(); - } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS)) { - return (T) getAvailableStreamConfigurations(); - } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS)) { - return (T) getAvailableMinFrameDurations(); + } else if (key.equals(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)) { + return (T) getStreamConfigurationMap(); } // For other keys, get() falls back to getBase() @@ -231,50 +232,6 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return availableFormats; } - private int[] getAvailableStreamConfigurations() { - final int NUM_ELEMENTS_IN_CONFIG = 4; - int[] availableConfigs = - getBase(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS); - if (availableConfigs != null) { - if (availableConfigs.length % NUM_ELEMENTS_IN_CONFIG != 0) { - Log.w(TAG, "availableStreamConfigurations is malformed, length must be multiple" - + " of " + NUM_ELEMENTS_IN_CONFIG); - return availableConfigs; - } - - for (int i = 0; i < availableConfigs.length; i += NUM_ELEMENTS_IN_CONFIG) { - // JPEG has different value between native and managed side, need override. - if (availableConfigs[i] == NATIVE_JPEG_FORMAT) { - availableConfigs[i] = ImageFormat.JPEG; - } - } - } - - return availableConfigs; - } - - private long[] getAvailableMinFrameDurations() { - final int NUM_ELEMENTS_IN_DURATION = 4; - long[] availableMinDurations = - getBase(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS); - if (availableMinDurations != null) { - if (availableMinDurations.length % NUM_ELEMENTS_IN_DURATION != 0) { - Log.w(TAG, "availableStreamConfigurations is malformed, length must be multiple" - + " of " + NUM_ELEMENTS_IN_DURATION); - return availableMinDurations; - } - - for (int i = 0; i < availableMinDurations.length; i += NUM_ELEMENTS_IN_DURATION) { - // JPEG has different value between native and managed side, need override. - if (availableMinDurations[i] == NATIVE_JPEG_FORMAT) { - availableMinDurations[i] = ImageFormat.JPEG; - } - } - } - - return availableMinDurations; - } - private Face[] getFaces() { final int FACE_LANDMARK_SIZE = 6; @@ -374,6 +331,17 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return fixedFaceRectangles; } + private StreamConfigurationMap getStreamConfigurationMap() { + StreamConfiguration[] configurations = getBase( + CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + StreamConfigurationDuration[] minFrameDurations = getBase( + CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS); + StreamConfigurationDuration[] stallDurations = getBase( + CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS); + + return new StreamConfigurationMap(configurations, minFrameDurations, stallDurations); + } + private <T> void setBase(Key<T> key, T value) { int tag = key.getTag(); @@ -401,56 +369,12 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { return setAvailableFormats((int[]) value); } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) { return setFaceRectangles((Rect[]) value); - } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS)) { - return setAvailableStreamConfigurations((int[])value); - } else if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS)) { - return setAvailableMinFrameDurations((long[])value); } // For other keys, set() falls back to setBase(). return false; } - private boolean setAvailableStreamConfigurations(int[] value) { - final int NUM_ELEMENTS_IN_CONFIG = 4; - int[] availableConfigs = value; - if (value == null) { - // Let setBase() to handle the null value case. - return false; - } - - int[] newValues = new int[availableConfigs.length]; - for (int i = 0; i < availableConfigs.length; i++) { - newValues[i] = availableConfigs[i]; - if (i % NUM_ELEMENTS_IN_CONFIG == 0 && availableConfigs[i] == ImageFormat.JPEG) { - newValues[i] = NATIVE_JPEG_FORMAT; - } - } - - setBase(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS, newValues); - return true; - } - - private boolean setAvailableMinFrameDurations(long[] value) { - final int NUM_ELEMENTS_IN_DURATION = 4; - long[] availableDurations = value; - if (value == null) { - // Let setBase() to handle the null value case. - return false; - } - - long[] newValues = new long[availableDurations.length]; - for (int i = 0; i < availableDurations.length; i++) { - newValues[i] = availableDurations[i]; - if (i % NUM_ELEMENTS_IN_DURATION == 0 && availableDurations[i] == ImageFormat.JPEG) { - newValues[i] = NATIVE_JPEG_FORMAT; - } - } - - setBase(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS, newValues); - return true; - } - private boolean setAvailableFormats(int[] value) { int[] availableFormat = value; if (value == null) { diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java index 3025cb4..98a7ad7 100644 --- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableReprocessFormatsMap.java @@ -15,9 +15,10 @@ */ package android.hardware.camera2.marshal.impl; -import android.hardware.camera2.ReprocessFormatsMap; import android.hardware.camera2.marshal.Marshaler; import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.params.ReprocessFormatsMap; +import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.TypeReference; import static android.hardware.camera2.impl.CameraMetadataNative.*; @@ -50,12 +51,13 @@ public class MarshalQueryableReprocessFormatsMap * INPUT_FORMAT, OUTPUT_FORMAT_COUNT, [OUTPUT_0, OUTPUT_1, ..., OUTPUT_FORMAT_COUNT-1] * }; */ - int[] inputs = value.getInputs(); + int[] inputs = StreamConfigurationMap.imageFormatToInternal(value.getInputs()); for (int input : inputs) { // INPUT_FORMAT buffer.putInt(input); - int[] outputs = value.getOutputs(input); + int[] outputs = + StreamConfigurationMap.imageFormatToInternal(value.getOutputs(input)); // OUTPUT_FORMAT_COUNT buffer.putInt(outputs.length); diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java index 6a4e821..62ace31 100644 --- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfiguration.java @@ -15,9 +15,9 @@ */ package android.hardware.camera2.marshal.impl; -import android.hardware.camera2.StreamConfiguration; import android.hardware.camera2.marshal.Marshaler; import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.params.StreamConfiguration; import android.hardware.camera2.utils.TypeReference; import static android.hardware.camera2.impl.CameraMetadataNative.*; diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java index c3d564e..fd3dfac 100644 --- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableStreamConfigurationDuration.java @@ -15,9 +15,9 @@ */ package android.hardware.camera2.marshal.impl; -import android.hardware.camera2.StreamConfigurationDuration; import android.hardware.camera2.marshal.Marshaler; import android.hardware.camera2.marshal.MarshalQueryable; +import android.hardware.camera2.params.StreamConfigurationDuration; import android.hardware.camera2.utils.TypeReference; import static android.hardware.camera2.impl.CameraMetadataNative.*; diff --git a/core/java/android/hardware/camera2/ReprocessFormatsMap.java b/core/java/android/hardware/camera2/params/ReprocessFormatsMap.java index 894a499..d3f5bc3 100644 --- a/core/java/android/hardware/camera2/ReprocessFormatsMap.java +++ b/core/java/android/hardware/camera2/params/ReprocessFormatsMap.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package android.hardware.camera2; +package android.hardware.camera2.params; import static com.android.internal.util.Preconditions.*; +import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.utils.HashCodeHelpers; import java.util.Arrays; @@ -61,9 +62,12 @@ public final class ReprocessFormatsMap { * @throws IllegalArgumentException * if the data was poorly formatted * (missing output format length or too few output formats) + * or if any of the input/formats were not valid * @throws NullPointerException * if entry was null * + * @see StreamConfigurationMap#checkArgumentFormatInternal + * * @hide */ public ReprocessFormatsMap(final int[] entry) { @@ -72,26 +76,31 @@ public final class ReprocessFormatsMap { int numInputs = 0; int left = entry.length; for (int i = 0; i < entry.length; ) { - final int format = entry[i]; + int inputFormat = StreamConfigurationMap.checkArgumentFormatInternal(entry[i]); left--; i++; if (left < 1) { throw new IllegalArgumentException( - String.format("Input %x had no output format length listed", format)); + String.format("Input %x had no output format length listed", inputFormat)); } final int length = entry[i]; left--; i++; + for (int j = 0; j < length; ++j) { + int outputFormat = entry[i + j]; + StreamConfigurationMap.checkArgumentFormatInternal(outputFormat); + } + if (length > 0) { if (left < length) { throw new IllegalArgumentException( String.format( "Input %x had too few output formats listed (actual: %d, " + - "expected: %d)", format, left, length)); + "expected: %d)", inputFormat, left, length)); } i += length; @@ -131,7 +140,6 @@ public final class ReprocessFormatsMap { throw new AssertionError( String.format("Input %x had no output format length listed", format)); } - // TODO: check format is a valid input format final int length = mEntry[i]; left--; @@ -149,12 +157,10 @@ public final class ReprocessFormatsMap { left -= length; } - // TODO: check output format is a valid output format - inputs[j] = format; } - return inputs; + return StreamConfigurationMap.imageFormatToPublic(inputs); } /** @@ -204,7 +210,7 @@ public final class ReprocessFormatsMap { outputs[k] = mEntry[i + k]; } - return outputs; + return StreamConfigurationMap.imageFormatToPublic(outputs); } i += length; diff --git a/core/java/android/hardware/camera2/StreamConfiguration.java b/core/java/android/hardware/camera2/params/StreamConfiguration.java index a514034..1c6b6e9 100644 --- a/core/java/android/hardware/camera2/StreamConfiguration.java +++ b/core/java/android/hardware/camera2/params/StreamConfiguration.java @@ -14,13 +14,16 @@ * limitations under the License. */ -package android.hardware.camera2; +package android.hardware.camera2.params; import static com.android.internal.util.Preconditions.*; -import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal; +import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormatInternal; import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; import android.hardware.camera2.utils.HashCodeHelpers; +import android.graphics.PixelFormat; import android.util.Size; /** @@ -62,11 +65,12 @@ public final class StreamConfiguration { } /** - * Get the image {@code format} in this stream configuration. + * Get the internal image {@code format} in this stream configuration. * * @return an integer format * * @see ImageFormat + * @see PixelFormat */ public final int getFormat() { return mFormat; diff --git a/core/java/android/hardware/camera2/StreamConfigurationDuration.java b/core/java/android/hardware/camera2/params/StreamConfigurationDuration.java index 6a31156..217059d 100644 --- a/core/java/android/hardware/camera2/StreamConfigurationDuration.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationDuration.java @@ -14,13 +14,15 @@ * limitations under the License. */ -package android.hardware.camera2; +package android.hardware.camera2.params; import static com.android.internal.util.Preconditions.*; -import static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal; +import static android.hardware.camera2.params.StreamConfigurationMap.checkArgumentFormatInternal; import android.graphics.ImageFormat; +import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.utils.HashCodeHelpers; +import android.graphics.PixelFormat; import android.util.Size; /** @@ -59,11 +61,12 @@ public final class StreamConfigurationDuration { } /** - * Get the image {@code format} in this stream configuration duration + * Get the internal image {@code format} in this stream configuration duration * * @return an integer format * * @see ImageFormat + * @see PixelFormat */ public final int getFormat() { return mFormat; diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java new file mode 100644 index 0000000..4cd6d15 --- /dev/null +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -0,0 +1,949 @@ +/* + * 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.camera2.params; + +import android.graphics.ImageFormat; +import android.graphics.PixelFormat; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.utils.HashCodeHelpers; +import android.view.Surface; +import android.util.Log; +import android.util.Size; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Objects; + +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}. + * <!-- TODO: link to input stream configuration --> + * + * <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively + * for that format) that are supported by a camera device.</p> + * + * <p>This also contains the minimum frame durations and stall durations for each format/size + * combination that can be used to calculate effective frame rate when submitting multiple captures. + * </p> + * + * <p>An instance of this object is available from {@link CameraCharacteristics} using + * the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP} key and the + * {@link CameraCharacteristics#get} method.</p> + * + * <pre><code>{@code + * CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); + * StreamConfigurationMap configs = characteristics.get( + * CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + * }</code></pre> + * + * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP + * @see CameraDevice#configureOutputs + */ +public final class StreamConfigurationMap { + + private static final String TAG = "StreamConfigurationMap"; + /** + * Create a new {@link StreamConfigurationMap}. + * + * <p>The array parameters ownership is passed to this object after creation; do not + * write to them after this constructor is invoked.</p> + * + * @param configurations a non-{@code null} array of {@link StreamConfiguration} + * @param minFrameDurations a non-{@code null} array of {@link StreamConfigurationDuration} + * @param stallDurations a non-{@code null} array of {@link StreamConfigurationDuration} + * + * @throws NullPointerException if any of the arguments or subelements were {@code null} + * + * @hide + */ + public StreamConfigurationMap( + StreamConfiguration[] configurations, + StreamConfigurationDuration[] minFrameDurations, + StreamConfigurationDuration[] stallDurations) { + + mConfigurations = checkArrayElementsNotNull(configurations, "configurations"); + mMinFrameDurations = checkArrayElementsNotNull(minFrameDurations, "minFrameDurations"); + mStallDurations = checkArrayElementsNotNull(stallDurations, "stallDurations"); + + // For each format, track how many sizes there are available to configure + for (StreamConfiguration config : configurations) { + HashMap<Integer, Integer> map = config.isOutput() ? mOutputFormats : mInputFormats; + + Integer count = map.get(config.getFormat()); + + if (count == null) { + count = 0; + } + count = count + 1; + + map.put(config.getFormat(), count); + } + + if (!mOutputFormats.containsKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)) { + throw new AssertionError( + "At least one stream configuration for IMPLEMENTATION_DEFINED must exist"); + } + } + + /** + * Get the image {@code format} output formats in this stream configuration. + * + * <p>All image formats returned by this function will be defined in either {@link ImageFormat} + * or in {@link PixelFormat} (and there is no possibility of collision).</p> + * + * <p>Formats listed in this array are guaranteed to return true if queried with + * {@link #isOutputSupportedFor(int).</p> + * + * @return an array of integer format + * + * @see ImageFormat + * @see PixelFormat + */ + public final int[] getOutputFormats() { + return getPublicFormats(/*output*/true); + } + + /** + * Get the image {@code format} input formats in this stream configuration. + * + * <p>All image formats returned by this function will be defined in either {@link ImageFormat} + * or in {@link PixelFormat} (and there is no possibility of collision).</p> + * + * @return an array of integer format + * + * @see ImageFormat + * @see PixelFormat + * + * @hide + */ + public final int[] getInputFormats() { + return getPublicFormats(/*output*/false); + } + + /** + * Get the supported input sizes for this input format. + * + * <p>The format must have come from {@link #getInputFormats}; otherwise + * {@code null} is returned.</p> + * + * @param format a format from {@link #getInputFormats} + * @return a non-empty array of sizes, or {@code null} if the format was not available. + * + * @hide + */ + public Size[] getInputSizes(final int format) { + return getPublicFormatSizes(format, /*output*/false); + } + + /** + * Determine whether or not output streams can be + * {@link CameraDevice#configureOutputs configured} with a particular user-defined format. + * + * <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}. + * Refer to the class which provides the surface for additional documentation.</p> + * + * <p>Formats for which this returns {@code true} are guaranteed to exist in the result + * returned by {@link #getOutputSizes}.</p> + * + * @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} + * + * @throws IllegalArgumentException + * if the image format was not a defined named constant + * from either {@link ImageFormat} or {@link PixelFormat} + * + * @see ImageFormat + * @see PixelFormat + * @see CameraDevice#configureOutputs + */ + public boolean isOutputSupportedFor(int format) { + checkArgumentFormat(format); + + format = imageFormatToInternal(format); + return getFormatsMap(/*output*/true).containsKey(format); + } + + /** + * Determine whether or not output streams can be configured with a particular class + * as a consumer. + * + * <p>The following list is generally usable for outputs: + * <ul> + * <li>{@link android.media.ImageReader} - + * Recommended for image processing or streaming to external resources (such as a file or + * network) + * <li>{@link android.media.MediaRecorder} - + * Recommended for recording video (simple to use) + * <li>{@link android.media.MediaCodec} - + * Recommended for recording video (more complicated to use, with more flexibility) + * <li>{@link android.renderscript.Allocation} - + * Recommended for image processing with {@link android.renderscript RenderScript} + * <li>{@link android.view.SurfaceHolder} - + * Recommended for low-power camera preview with {@link android.view.SurfaceView} + * <li>{@link android.graphics.SurfaceTexture} - + * Recommended for OpenGL-accelerated preview processing or compositing with + * {@link android.view.TextureView} + * </ul> + * </p> + * + * <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> + * + * <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> + * + * @param klass a non-{@code null} {@link Class} object reference + * @return {@code true} if this class is supported as an output, {@code false} otherwise + * + * @throws NullPointerException if {@code klass} was {@code null} + * + * @see CameraDevice#configureOutputs + * @see #isOutputSupportedFor(Surface) + */ + public static <T> boolean isOutputSupportedFor(Class<T> klass) { + checkNotNull(klass, "klass must not be null"); + + if (klass == android.media.ImageReader.class) { + return true; + } else if (klass == android.media.MediaRecorder.class) { + return true; + } else if (klass == android.media.MediaCodec.class) { + return true; + } else if (klass == android.renderscript.Allocation.class) { + return true; + } else if (klass == android.view.SurfaceHolder.class) { + return true; + } else if (klass == android.graphics.SurfaceTexture.class) { + return true; + } + + return false; + } + + /** + * Determine whether or not the {@code surface} in its current state is suitable to be + * {@link CameraDevice#configureOutputs configured} 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 + * compatible with the {@link CameraDevice} in general + * (see {@link #isOutputSupportedFor(Class)}, but it is the caller's responsibility to put the + * {@code surface} into a state that will be compatible with the {@link CameraDevice}.</p> + * + * <p>Reasons for a {@code surface} being specifically incompatible might be: + * <ul> + * <li>Using a format that's not listed by {@link #getOutputFormats} + * <li>Using a format/size combination that's not listed by {@link #getOutputSizes} + * <li>The {@code surface} itself is not in a state where it can service a new producer.</p> + * </li> + * </ul> + * + * This is not an exhaustive list; see the particular class's documentation for further + * possible reasons of incompatibility.</p> + * + * @param surface a non-{@code null} {@link Surface} object reference + * @return {@code true} if this is supported, {@code false} otherwise + * + * @throws NullPointerException if {@code surface} was {@code null} + * + * @see CameraDevice#configureOutputs + * @see #isOutputSupportedFor(Class) + */ + public boolean isOutputSupportedFor(Surface surface) { + checkNotNull(surface, "surface must not be null"); + + throw new UnsupportedOperationException("Not implemented yet"); + + // TODO: JNI function that checks the Surface's IGraphicBufferProducer state + } + + /** + * Get a list of sizes compatible with {@code klass} to use as an output. + * + * <p>Since some of the supported classes may support additional formats beyond + * an opaque/implementation-defined (under-the-hood) format; this function only returns + * sizes for the implementation-defined format.</p> + * + * <p>Some classes such as {@link android.media.ImageReader} may only support user-defined + * formats; in particular {@link #isOutputSupportedFor(Class)} will return {@code true} for + * that class and this method will return an empty array (but not {@code null}).</p> + * + * <p>If a well-defined format such as {@code NV21} is required, use + * {@link #getOutputSizes(int)} instead.</p> + * + * <p>The {@code klass} should be a supported output, that querying + * {@code #isOutputSupportedFor(Class)} should return {@code true}.</p> + * + * @param klass + * a non-{@code null} {@link Class} object reference + * @return + * an array of supported sizes for implementation-defined formats, + * or {@code null} iff the {@code klass} is not a supported output + * + * @throws NullPointerException if {@code klass} was {@code null} + * + * @see #isOutputSupportedFor(Class) + */ + public <T> Size[] getOutputSizes(Class<T> klass) { + if (isOutputSupportedFor(klass) == false) { + return null; + } + + return getInternalFormatSizes(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, /*output*/true); + } + + /** + * Get a list of sizes compatible with the requested image {@code format}. + * + * <p>The {@code format} should be a supported format (one of the formats returned by + * {@link #getOutputFormats}).</p> + * + * @param format an image format from {@link ImageFormat} or {@link PixelFormat} + * @return + * an array of supported sizes, + * or {@code null} if the {@code format} is not a supported output + * + * @see ImageFormat + * @see PixelFormat + * @see #getOutputFormats + */ + public Size[] getOutputSizes(int format) { + return getPublicFormatSizes(format, /*output*/true); + } + + /** + * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration} + * for the format/size combination (in nanoseconds). + * + * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p> + * <p>{@code size} should be one of the ones returned by + * {@link #getOutputSizes(int)}.</p> + * + * <p>This should correspond to the frame duration when only that stream is active, with all + * processing (typically in {@code android.*.mode}) set to either {@code OFF} or {@code FAST}. + * </p> + * + * <p>When multiple streams are used in a request, the minimum frame duration will be + * {@code max(individual stream min durations)}.</p> + * + * <!-- + * TODO: uncomment after adding input stream support + * <p>The minimum frame duration of a stream (of a particular format, size) is the same + * regardless of whether the stream is input or output.</p> + * --> + * + * @param format an image format from {@link ImageFormat} or {@link PixelFormat} + * @param size an output-compatible size + * @return a minimum frame duration {@code >=} 0 in nanoseconds + * + * @throws IllegalArgumentException if {@code format} or {@code size} was not supported + * @throws NullPointerException if {@code size} was {@code null} + * + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see #getOutputStallDuration(int, Size) + * @see ImageFormat + * @see PixelFormat + */ + public long getOutputMinFrameDuration(int format, Size size) { + checkNotNull(size, "size must not be null"); + checkArgumentFormatSupported(format, /*output*/true); + + return getInternalFormatDuration(imageFormatToInternal(format), size, DURATION_MIN_FRAME); + } + + /** + * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration} + * for the class/size combination (in nanoseconds). + * + * <p>This assumes a the {@code klass} is set up to use an implementation-defined format. + * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p> + * + * <p>{@code klass} should be one of the ones which is supported by + * {@link #isOutputSupportedFor(Class)}.</p> + * + * <p>{@code size} should be one of the ones returned by + * {@link #getOutputSizes(int)}.</p> + * + * <p>This should correspond to the frame duration when only that stream is active, with all + * processing (typically in {@code android.*.mode}) set to either {@code OFF} or {@code FAST}. + * </p> + * + * <p>When multiple streams are used in a request, the minimum frame duration will be + * {@code max(individual stream min durations)}.</p> + * + * <!-- + * TODO: uncomment after adding input stream support + * <p>The minimum frame duration of a stream (of a particular format, size) is the same + * regardless of whether the stream is input or output.</p> + * --> + * + * @param klass + * a class which is supported by {@link #isOutputSupportedFor(Class)} and has a + * non-empty array returned by {@link #getOutputSizes(Class)} + * @param size an output-compatible size + * @return a minimum frame duration {@code >=} 0 in nanoseconds + * + * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported + * @throws NullPointerException if {@code size} or {@code klass} was {@code null} + * + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see ImageFormat + * @see PixelFormat + */ + public <T> long getOutputMinFrameDuration(final Class<T> klass, final Size size) { + if (!isOutputSupportedFor(klass)) { + throw new IllegalArgumentException("klass was not supported"); + } + + return getInternalFormatDuration(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, + size, DURATION_MIN_FRAME); + } + + /** + * Get the stall duration for the format/size combination (in nanoseconds). + * + * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p> + * <p>{@code size} should be one of the ones returned by + * {@link #getOutputSizes(int)}.</p> + * + * <p> + * A stall duration is how much extra time would get added to the normal minimum frame duration + * for a repeating request that has streams with non-zero stall. + * + * <p>For example, consider JPEG captures which have the following characteristics: + * + * <ul> + * <li>JPEG streams act like processed YUV streams in requests for which they are not included; + * in requests in which they are directly referenced, they act as JPEG streams. + * This is because supporting a JPEG stream requires the underlying YUV data to always be ready + * for use by a JPEG encoder, but the encoder will only be used (and impact frame duration) on + * requests that actually reference a JPEG stream. + * <li>The JPEG processor can run concurrently to the rest of the camera pipeline, but cannot + * process more than 1 capture at a time. + * </ul> + * + * <p>In other words, using a repeating YUV request would result in a steady frame rate + * (let's say it's 30 FPS). If a single JPEG request is submitted periodically, + * the frame rate will stay at 30 FPS (as long as we wait for the previous JPEG to return each + * time). If we try to submit a repeating YUV + JPEG request, then the frame rate will drop from + * 30 FPS.</p> + * + * <p>In general, submitting a new request with a non-0 stall time stream will <em>not</em> cause a + * frame rate drop unless there are still outstanding buffers for that stream from previous + * requests.</p> + * + * <p>Submitting a repeating request with streams (call this {@code S}) is the same as setting + * the minimum frame duration from the normal minimum frame duration corresponding to {@code S}, + * added with the maximum stall duration for {@code S}.</p> + * + * <p>If interleaving requests with and without a stall duration, a request will stall by the + * maximum of the remaining times for each can-stall stream with outstanding buffers.</p> + * + * <p>This means that a stalling request will not have an exposure start until the stall has + * completed.</p> + * + * <p>This should correspond to the stall duration when only that stream is active, with all + * processing (typically in {@code android.*.mode}) set to {@code FAST} or {@code OFF}. + * Setting any of the processing modes to {@code HIGH_QUALITY} effectively results in an + * indeterminate stall duration for all streams in a request (the regular stall calculation + * rules are ignored).</p> + * + * <p>The following formats may always have a stall duration: + * <ul> + * <li>{@link ImageFormat#JPEG JPEG} + * <li>{@link ImageFormat#RAW_SENSOR RAW16} + * </ul> + * </p> + * + * <p>The following formats will never have a stall duration: + * <ul> + * <li>{@link ImageFormat#YUV_420_888 YUV_420_888} + * <li>{@link #isOutputSupportedFor(Class) Implementation-Defined} + * </ul></p> + * + * <p> + * All other formats may or may not have an allowed stall duration on a per-capability basis; + * refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * android.request.availableCapabilities} for more details.</p> + * </p> + * + * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} + * for more information about calculating the max frame rate (absent stalls).</p> + * + * @param format an image format from {@link ImageFormat} or {@link PixelFormat} + * @param size an output-compatible size + * @return a stall duration {@code >=} 0 in nanoseconds + * + * @throws IllegalArgumentException if {@code format} or {@code size} was not supported + * @throws NullPointerException if {@code size} was {@code null} + * + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see ImageFormat + * @see PixelFormat + */ + public long getOutputStallDuration(int format, Size size) { + checkArgumentFormatSupported(format, /*output*/true); + + return getInternalFormatDuration(imageFormatToInternal(format), + size, DURATION_STALL); + } + + /** + * Get the stall duration for the class/size combination (in nanoseconds). + * + * <p>This assumes a the {@code klass} is set up to use an implementation-defined format. + * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p> + * + * <p>{@code klass} should be one of the ones with a non-empty array returned by + * {@link #getOutputSizes(Class)}.</p> + * + * <p>{@code size} should be one of the ones returned by + * {@link #getOutputSizes(Class)}.</p> + * + * <p>See {@link #getOutputStallDuration(int, Size)} for a definition of a + * <em>stall duration</em>.</p> + * + * @param klass + * a class which is supported by {@link #isOutputSupportedFor(Class)} and has a + * non-empty array returned by {@link #getOutputSizes(Class)} + * @param size an output-compatible size + * @return a minimum frame duration {@code >=} 0 in nanoseconds + * + * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported + * @throws NullPointerException if {@code size} or {@code klass} was {@code null} + * + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see ImageFormat + * @see PixelFormat + */ + public <T> long getOutputStallDuration(final Class<T> klass, final Size size) { + if (!isOutputSupportedFor(klass)) { + throw new IllegalArgumentException("klass was not supported"); + } + + return getInternalFormatDuration(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, + size, DURATION_STALL); + } + + /** + * Check if this {@link StreamConfigurationMap} is equal to another + * {@link StreamConfigurationMap}. + * + * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p> + * + * @return {@code true} if the objects were equal, {@code false} otherwise + */ + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof StreamConfigurationMap) { + final StreamConfigurationMap other = (StreamConfigurationMap) obj; + // XX: do we care about order? + return Arrays.equals(mConfigurations, other.mConfigurations) && + Arrays.equals(mMinFrameDurations, other.mMinFrameDurations) && + Arrays.equals(mStallDurations, other.mStallDurations); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + // XX: do we care about order? + return HashCodeHelpers.hashCode(mConfigurations, mMinFrameDurations, mStallDurations); + } + + // Check that the argument is supported by #getOutputFormats or #getInputFormats + private int checkArgumentFormatSupported(int format, boolean output) { + checkArgumentFormat(format); + + int[] formats = output ? getOutputFormats() : getInputFormats(); + for (int i = 0; i < formats.length; ++i) { + if (format == formats[i]) { + return format; + } + } + + throw new IllegalArgumentException(String.format( + "format %x is not supported by this stream configuration map", format)); + } + + /** + * Ensures that the format is either user-defined or implementation defined. + * + * <p>If a format has a different internal representation than the public representation, + * passing in the public representation here will fail.</p> + * + * <p>For example if trying to use {@link ImageFormat#JPEG}: + * it has a different public representation than the internal representation + * {@code HAL_PIXEL_FORMAT_BLOB}, this check will fail.</p> + * + * <p>Any invalid/undefined formats will raise an exception.</p> + * + * @param format image format + * @return the format + * + * @throws IllegalArgumentException if the format was invalid + */ + static int checkArgumentFormatInternal(int format) { + switch (format) { + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + case HAL_PIXEL_FORMAT_BLOB: + return format; + case ImageFormat.JPEG: + throw new IllegalArgumentException( + "ImageFormat.JPEG is an unknown internal format"); + default: + return checkArgumentFormat(format); + } + } + + /** + * Ensures that the format is publicly user-defined in either ImageFormat or PixelFormat. + * + * <p>If a format has a different public representation than the internal representation, + * passing in the internal representation here will fail.</p> + * + * <p>For example if trying to use {@code HAL_PIXEL_FORMAT_BLOB}: + * it has a different internal representation than the public representation + * {@link ImageFormat#JPEG}, this check will fail.</p> + * + * <p>Any invalid/undefined formats will raise an exception, including implementation-defined. + * </p> + * + * <p>Note that {@code @hide} and deprecated formats will not pass this check.</p> + * + * @param format image format + * @return the format + * + * @throws IllegalArgumentException if the format was not user-defined + */ + static int checkArgumentFormat(int format) { + // TODO: remove this hack , CTS shouldn't have been using internal constants + if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) { + Log.w(TAG, "RAW_OPAQUE is not yet a published format; allowing it anyway"); + return format; + } + + if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { + throw new IllegalArgumentException(String.format( + "format 0x%x was not defined in either ImageFormat or PixelFormat", format)); + } + + return format; + } + + /** + * Convert a public-visible {@code ImageFormat} into an internal format + * compatible with {@code graphics.h}. + * + * <p>In particular these formats are converted: + * <ul> + * <li>HAL_PIXEL_FORMAT_BLOB => ImageFormat.JPEG + * </ul> + * </p> + * + * <p>Passing in an implementation-defined format which has no public equivalent will fail; + * as will passing in a public format which has a different internal format equivalent. + * See {@link #checkArgumentFormat} for more details about a legal public format.</p> + * + * <p>All other formats are returned as-is, no further invalid check is performed.</p> + * + * <p>This function is the dual of {@link #imageFormatToInternal}.</p> + * + * @param format image format from {@link ImageFormat} or {@link PixelFormat} + * @return the converted image formats + * + * @throws IllegalArgumentException + * if {@code format} is {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED} or + * {@link ImageFormat#JPEG} + * + * @see ImageFormat + * @see PixelFormat + * @see #checkArgumentFormat + */ + static int imageFormatToPublic(int format) { + switch (format) { + case HAL_PIXEL_FORMAT_BLOB: + return ImageFormat.JPEG; + case ImageFormat.JPEG: + throw new IllegalArgumentException( + "ImageFormat.JPEG is an unknown internal format"); + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + throw new IllegalArgumentException( + "IMPLEMENTATION_DEFINED must not leak to public API"); + default: + return format; + } + } + + /** + * Convert image formats from internal to public formats (in-place). + * + * @param formats an array of image formats + * @return {@code formats} + * + * @see #imageFormatToPublic + */ + static int[] imageFormatToPublic(int[] formats) { + if (formats == null) { + return null; + } + + for (int i = 0; i < formats.length; ++i) { + formats[i] = imageFormatToPublic(formats[i]); + } + + return formats; + } + + /** + * Convert a public format compatible with {@code ImageFormat} to an internal format + * from {@code graphics.h}. + * + * <p>In particular these formats are converted: + * <ul> + * <li>ImageFormat.JPEG => HAL_PIXEL_FORMAT_BLOB + * </ul> + * </p> + * + * <p>Passing in an implementation-defined format here will fail (it's not a public format); + * as will passing in an internal format which has a different public format equivalent. + * See {@link #checkArgumentFormat} for more details about a legal public format.</p> + * + * <p>All other formats are returned as-is, no invalid check is performed.</p> + * + * <p>This function is the dual of {@link #imageFormatToPublic}.</p> + * + * @param format public image format from {@link ImageFormat} or {@link PixelFormat} + * @return the converted image formats + * + * @see ImageFormat + * @see PixelFormat + * + * @throws IllegalArgumentException + * if {@code format} was {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED} + */ + static int imageFormatToInternal(int format) { + switch (format) { + case ImageFormat.JPEG: + return HAL_PIXEL_FORMAT_BLOB; + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + throw new IllegalArgumentException( + "IMPLEMENTATION_DEFINED is not allowed via public API"); + default: + return format; + } + } + + /** + * Convert image formats from public to internal formats (in-place). + * + * @param formats an array of image formats + * @return {@code formats} + * + * @see #imageFormatToInternal + * + * @hide + */ + public static int[] imageFormatToInternal(int[] formats) { + if (formats == null) { + return null; + } + + for (int i = 0; i < formats.length; ++i) { + formats[i] = imageFormatToInternal(formats[i]); + } + + return formats; + } + + private Size[] getPublicFormatSizes(int format, boolean output) { + try { + checkArgumentFormatSupported(format, output); + } catch (IllegalArgumentException e) { + return null; + } + + format = imageFormatToInternal(format); + + return getInternalFormatSizes(format, output); + } + + private Size[] getInternalFormatSizes(int format, boolean output) { + HashMap<Integer, Integer> formatsMap = getFormatsMap(output); + + Integer sizesCount = formatsMap.get(format); + if (sizesCount == null) { + throw new IllegalArgumentException("format not available"); + } + + int len = sizesCount; + Size[] sizes = new Size[len]; + int sizeIndex = 0; + + for (StreamConfiguration config : mConfigurations) { + if (config.getFormat() == format && config.isOutput() == output) { + sizes[sizeIndex++] = config.getSize(); + } + } + + if (sizeIndex != len) { + throw new AssertionError( + "Too few sizes (expected " + len + ", actual " + sizeIndex + ")"); + } + + return sizes; + } + + /** Get the list of publically visible output formats; does not include IMPL_DEFINED */ + private int[] getPublicFormats(boolean output) { + int[] formats = new int[getPublicFormatCount(output)]; + + int i = 0; + + for (int format : getFormatsMap(output).keySet()) { + if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { + formats[i++] = format; + } + } + + if (formats.length != i) { + throw new AssertionError("Too few formats " + i + ", expected " + formats.length); + } + + return imageFormatToPublic(formats); + } + + /** Get the format -> size count map for either output or input formats */ + private HashMap<Integer, Integer> getFormatsMap(boolean output) { + return output ? mOutputFormats : mInputFormats; + } + + private long getInternalFormatDuration(int format, Size size, int duration) { + // assume format is already checked, since its internal + + if (!arrayContains(getInternalFormatSizes(format, /*output*/true), size)) { + throw new IllegalArgumentException("size was not supported"); + } + + StreamConfigurationDuration[] durations = getDurations(duration); + + for (StreamConfigurationDuration configurationDuration : durations) { + if (configurationDuration.getFormat() == format && + configurationDuration.getWidth() == size.getWidth() && + configurationDuration.getHeight() == size.getHeight()) { + return configurationDuration.getDuration(); + } + } + + return getDurationDefault(duration); + } + + /** + * Get the durations array for the kind of duration + * + * @see #DURATION_MIN_FRAME + * @see #DURATION_STALL + * */ + private StreamConfigurationDuration[] getDurations(int duration) { + switch (duration) { + case DURATION_MIN_FRAME: + return mMinFrameDurations; + case DURATION_STALL: + return mStallDurations; + default: + throw new IllegalArgumentException("duration was invalid"); + } + } + + private long getDurationDefault(int duration) { + switch (duration) { + case DURATION_MIN_FRAME: + throw new AssertionError("Minimum frame durations are required to be listed"); + case DURATION_STALL: + return 0L; // OK. A lack of a stall duration implies a 0 stall duration + default: + throw new IllegalArgumentException("duration was invalid"); + } + } + + /** Count the number of publicly-visible output formats */ + private int getPublicFormatCount(boolean output) { + HashMap<Integer, Integer> formatsMap = getFormatsMap(output); + + int size = formatsMap.size(); + if (formatsMap.containsKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)) { + size -= 1; + } + return size; + } + + private static <T> boolean arrayContains(T[] array, T element) { + if (array == null) { + return false; + } + + for (T el : array) { + if (Objects.equals(el, element)) { + return true; + } + } + + return false; + } + + // from system/core/include/system/graphics.h + private static final int HAL_PIXEL_FORMAT_BLOB = 0x21; + private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22; + private static final int HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24; + + /** + * @see #getDurations(int) + * @see #getDurationDefault(int) + */ + private static final int DURATION_MIN_FRAME = 0; + private static final int DURATION_STALL = 1; + + private final StreamConfiguration[] mConfigurations; + private final StreamConfigurationDuration[] mMinFrameDurations; + private final StreamConfigurationDuration[] mStallDurations; + + /** ImageFormat -> num output sizes mapping */ + private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mOutputFormats = + new HashMap<Integer, Integer>(); + /** ImageFormat -> num input sizes mapping */ + private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mInputFormats = + new HashMap<Integer, Integer>(); + +} diff --git a/core/java/android/hardware/camera2/LongParcelable.aidl b/core/java/android/hardware/camera2/utils/LongParcelable.aidl index 7d7e51b..98ad1b2 100644 --- a/core/java/android/hardware/camera2/LongParcelable.aidl +++ b/core/java/android/hardware/camera2/utils/LongParcelable.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.hardware.camera2; +package android.hardware.camera2.utils; /** @hide */ -parcelable LongParcelable;
\ No newline at end of file +parcelable LongParcelable; diff --git a/core/java/android/hardware/camera2/LongParcelable.java b/core/java/android/hardware/camera2/utils/LongParcelable.java index 97b0631..c89b339 100644 --- a/core/java/android/hardware/camera2/LongParcelable.java +++ b/core/java/android/hardware/camera2/utils/LongParcelable.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.hardware.camera2; +package android.hardware.camera2.utils; import android.os.Parcel; import android.os.Parcelable; diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java index 7213c78..9193f89 100644 --- a/core/java/android/hardware/hdmi/HdmiCec.java +++ b/core/java/android/hardware/hdmi/HdmiCec.java @@ -120,7 +120,7 @@ public final class HdmiCec { public static final int MESSAGE_TIMER_CLEARED_STATUS = 0x043; public static final int MESSAGE_USER_CONTROL_PRESSED = 0x44; public static final int MESSAGE_USER_CONTROL_RELEASED = 0x45; - public static final int MESSAGE_GET_OSD_NAME = 0x46; + public static final int MESSAGE_GIVE_OSD_NAME = 0x46; public static final int MESSAGE_SET_OSD_NAME = 0x47; public static final int MESSAGE_SET_OSD_STRING = 0x64; public static final int MESSAGE_SET_TIMER_PROGRAM_TITLE = 0x67; @@ -158,6 +158,12 @@ public final class HdmiCec { public static final int MESSAGE_VENDOR_COMMAND_WITH_ID = 0xA0; public static final int MESSAGE_CLEAR_EXTERNAL_TIMER = 0xA1; public static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2; + public static final int MESSAGE_INITIATE_ARC = 0xC0; + public static final int MESSAGE_REPORT_ARC_INITIATED = 0xC1; + public static final int MESSAGE_REPORT_ARC_TERMINATED = 0xC2; + public static final int MESSAGE_REQUEST_ARC_INITIATION = 0xC3; + public static final int MESSAGE_REQUEST_ARC_TERMINATION = 0xC4; + public static final int MESSAGE_TERMINATE_ARC = 0xC5; public static final int MESSAGE_ABORT = 0xFF; public static final int UNKNOWN_VENDOR_ID = 0xFFFFFF; diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 0a09fcb..489b8a5 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -89,7 +89,6 @@ public class LinkProperties implements Parcelable { } public LinkProperties() { - clear(); } // copy constructor instead of clone diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 4b85398..c2b06a2 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -66,6 +66,7 @@ public abstract class NetworkAgent extends Handler { private AsyncChannel mAsyncChannel; private final String LOG_TAG; private static final boolean DBG = true; + private static final boolean VDBG = true; // TODO - this class shouldn't cache data or it runs the risk of getting out of sync // Make the API require each of these when any is updated so we have the data we need, // without caching. @@ -266,11 +267,14 @@ public abstract class NetworkAgent extends Handler { */ private void evalScores() { if (mConnectionRequested) { + if (VDBG) log("evalScores - already trying - size=" + mNetworkRequests.size()); // already trying return; } + if (VDBG) log("evalScores!"); for (int i=0; i < mNetworkRequests.size(); i++) { int score = mNetworkRequests.valueAt(i).score; + if (VDBG) log(" checking request Min " + score + " vs my score " + mNetworkScore); if (score < mNetworkScore) { // have a request that has a lower scored network servicing it // (or no network) than we could provide, so lets connect! diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index f339e52..32050dc 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -146,9 +146,9 @@ public class BatteryManager { return null; } - BatteryProperty prop = new BatteryProperty(Integer.MIN_VALUE); + BatteryProperty prop = new BatteryProperty(); if ((mBatteryPropertiesRegistrar.getProperty(id, prop) == 0) && - (prop.getInt() != Integer.MIN_VALUE)) + (prop.getLong() != Long.MIN_VALUE)) return prop; else return null; diff --git a/core/java/android/os/BatteryProperty.java b/core/java/android/os/BatteryProperty.java index ec73952..0ed856e 100644 --- a/core/java/android/os/BatteryProperty.java +++ b/core/java/android/os/BatteryProperty.java @@ -53,20 +53,18 @@ public class BatteryProperty implements Parcelable { */ public static final int CAPACITY = 4; - private int mValueInt; - /** - * @hide + * Battery remaining energy in nanowatt-hours, as a long integer. */ - public BatteryProperty(int value) { - mValueInt = value; - } + public static final int ENERGY_COUNTER = 4; + + private long mValueLong; /** * @hide */ public BatteryProperty() { - mValueInt = Integer.MIN_VALUE; + mValueLong = Long.MIN_VALUE; } /** @@ -79,9 +77,21 @@ public class BatteryProperty implements Parcelable { * @return The queried property value, or Integer.MIN_VALUE if not supported. */ public int getInt() { - return mValueInt; + return (int)mValueLong; } + /** + * Return the value of a property of long type previously queried + * via {@link BatteryManager#getProperty + * BatteryManager.getProperty()}. If the platform does + * not provide the property queried, this value will be + * Long.MIN_VALUE. + * + * @return The queried property value, or Long.MIN_VALUE if not supported. + */ + public long getLong() { + return mValueLong; + } /* * Parcel read/write code must be kept in sync with * frameworks/native/services/batteryservice/BatteryProperty.cpp @@ -92,11 +102,11 @@ public class BatteryProperty implements Parcelable { } public void readFromParcel(Parcel p) { - mValueInt = p.readInt(); + mValueLong = p.readLong(); } public void writeToParcel(Parcel p, int flags) { - p.writeInt(mValueInt); + p.writeLong(mValueLong); } public static final Parcelable.Creator<BatteryProperty> CREATOR diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 4857533..cf0caed 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -31,7 +31,6 @@ import android.telephony.SignalStrength; import android.text.format.DateFormat; import android.util.Printer; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.TimeUtils; import com.android.internal.os.BatterySipper; @@ -601,6 +600,7 @@ public abstract class BatteryStats implements Parcelable { public int states; public static final int STATE2_VIDEO_ON_FLAG = 1<<0; + public static final int STATE2_LOW_POWER_FLAG = 1<<1; public int states2; // The wake lock that was acquired at this point. @@ -622,8 +622,11 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_TOP = 0x0003; // Event is about an application package that is at the top of the screen. public static final int EVENT_SYNC = 0x0004; + // Events for all additional wake locks aquired/release within a wake block. + // These are not generated by default. + public static final int EVENT_WAKE_LOCK = 0x0005; // Number of event types. - public static final int EVENT_COUNT = 0x0005; + public static final int EVENT_COUNT = 0x0006; // Mask to extract out only the type part of the event. public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH); @@ -635,6 +638,8 @@ public abstract class BatteryStats implements Parcelable { public static final int EVENT_TOP_FINISH = EVENT_TOP | EVENT_FLAG_FINISH; public static final int EVENT_SYNC_START = EVENT_SYNC | EVENT_FLAG_START; public static final int EVENT_SYNC_FINISH = EVENT_SYNC | EVENT_FLAG_FINISH; + public static final int EVENT_WAKE_LOCK_START = EVENT_WAKE_LOCK | EVENT_FLAG_START; + public static final int EVENT_WAKE_LOCK_FINISH = EVENT_WAKE_LOCK | EVENT_FLAG_FINISH; // For CMD_EVENT. public int eventCode; @@ -887,6 +892,11 @@ public abstract class BatteryStats implements Parcelable { return true; } + public void removeEvents(int code) { + int idx = code&HistoryItem.EVENT_TYPE_MASK; + mActiveEvents[idx] = null; + } + public HashMap<String, SparseIntArray> getStateForEvent(int code) { return mActiveEvents[code]; } @@ -997,6 +1007,21 @@ public abstract class BatteryStats implements Parcelable { long elapsedRealtimeUs, int which); /** + * Returns the time in microseconds that low power mode has been enabled while the device was + * running on battery. + * + * {@hide} + */ + public abstract long getLowPowerModeEnabledTime(long elapsedRealtimeUs, int which); + + /** + * Returns the number of times that low power mode was enabled. + * + * {@hide} + */ + public abstract int getLowPowerModeEnabledCount(int which); + + /** * Returns the time in microseconds that the phone has been on while the device was * running on battery. * @@ -1157,14 +1182,15 @@ public abstract class BatteryStats implements Parcelable { public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS = new BitDescription[] { new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"), + new BitDescription(HistoryItem.STATE2_LOW_POWER_FLAG, "low_power", "lp"), }; public static final String[] HISTORY_EVENT_NAMES = new String[] { - "null", "proc", "fg", "top", "sync" + "null", "proc", "fg", "top", "sync", "wake_lock_in" }; public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] { - "Enl", "Epr", "Efg", "Etp", "Esy" + "Enl", "Epr", "Efg", "Etp", "Esy", "Ewl" }; /** @@ -1630,11 +1656,12 @@ public abstract class BatteryStats implements Parcelable { final long totalUptime = computeUptime(rawUptime, which); final long screenOnTime = getScreenOnTime(rawRealtime, which); final long interactiveTime = getInteractiveTime(rawRealtime, which); + final long lowPowerModeEnabledTime = getLowPowerModeEnabledTime(rawRealtime, which); final long phoneOnTime = getPhoneOnTime(rawRealtime, which); final long wifiOnTime = getWifiOnTime(rawRealtime, which); final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which); final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which); - + StringBuilder sb = new StringBuilder(128); SparseArray<? extends Uid> uidStats = getUidStats(); @@ -1699,7 +1726,8 @@ public abstract class BatteryStats implements Parcelable { mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes, fullWakeLockTimeTotal, partialWakeLockTimeTotal, 0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which), - getMobileRadioActiveAdjustedTime(which), interactiveTime / 1000); + getMobileRadioActiveAdjustedTime(which), interactiveTime / 1000, + lowPowerModeEnabledTime / 1000); // Dump screen brightness stats Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS]; @@ -2092,32 +2120,20 @@ public abstract class BatteryStats implements Parcelable { final long screenOnTime = getScreenOnTime(rawRealtime, which); final long interactiveTime = getInteractiveTime(rawRealtime, which); + final long lowPowerModeEnabledTime = getLowPowerModeEnabledTime(rawRealtime, which); final long phoneOnTime = getPhoneOnTime(rawRealtime, which); final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which); final long wifiOnTime = getWifiOnTime(rawRealtime, which); final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which); sb.setLength(0); sb.append(prefix); - sb.append(" Interactive: "); formatTimeMs(sb, interactiveTime / 1000); - sb.append("("); sb.append(formatRatioLocked(interactiveTime, whichBatteryRealtime)); - sb.append(")"); - pw.println(sb.toString()); - sb.setLength(0); - sb.append(prefix); sb.append(" Screen on: "); formatTimeMs(sb, screenOnTime / 1000); sb.append("("); sb.append(formatRatioLocked(screenOnTime, whichBatteryRealtime)); sb.append(") "); sb.append(getScreenOnCount(which)); - sb.append("x, Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000); - sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime)); + sb.append("x, Interactive: "); formatTimeMs(sb, interactiveTime / 1000); + sb.append("("); sb.append(formatRatioLocked(interactiveTime, whichBatteryRealtime)); sb.append(")"); pw.println(sb.toString()); - if (phoneOnTime != 0) { - sb.setLength(0); - sb.append(prefix); - sb.append(" Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000); - sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime)); - sb.append(") "); sb.append(getPhoneOnCount(which)); - } sb.setLength(0); sb.append(prefix); sb.append(" Screen brightnesses:"); @@ -2139,7 +2155,24 @@ public abstract class BatteryStats implements Parcelable { } if (!didOne) sb.append(" (no activity)"); pw.println(sb.toString()); - + if (lowPowerModeEnabledTime != 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Low power mode enabled: "); + formatTimeMs(sb, lowPowerModeEnabledTime / 1000); + sb.append("("); + sb.append(formatRatioLocked(lowPowerModeEnabledTime, whichBatteryRealtime)); + sb.append(")"); + pw.println(sb.toString()); + } + if (phoneOnTime != 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Active phone call: "); formatTimeMs(sb, phoneOnTime / 1000); + sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime)); + sb.append(") "); sb.append(getPhoneOnCount(which)); + } + // Calculate wakelock times across all uids. long fullWakeLockTimeTotalMicros = 0; long partialWakeLockTimeTotalMicros = 0; @@ -3004,6 +3037,8 @@ public abstract class BatteryStats implements Parcelable { pw.print(rec.numReadInts); pw.print(") "); } else { + pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); + pw.print(HISTORY_DATA); pw.print(','); if (lastTime < 0) { pw.print(rec.time - baseTime); } else { @@ -3190,6 +3225,7 @@ public abstract class BatteryStats implements Parcelable { } pw.println(); oldState = rec.states; + oldState2 = rec.states2; } } } @@ -3266,21 +3302,25 @@ public abstract class BatteryStats implements Parcelable { if (rec.cmd == HistoryItem.CMD_CURRENT_TIME || rec.cmd == HistoryItem.CMD_RESET) { printed = true; + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + rec.cmd = HistoryItem.CMD_UPDATE; } else if (rec.currentTime != 0) { printed = true; byte cmd = rec.cmd; rec.cmd = HistoryItem.CMD_CURRENT_TIME; - if (checkin) { - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - } hprinter.printNextItem(pw, rec, baseTime, checkin, (flags&DUMP_VERBOSE) != 0); rec.cmd = cmd; } if (tracker != null) { - int oldCode = rec.eventCode; - HistoryTag oldTag = rec.eventTag; + if (rec.cmd != HistoryItem.CMD_UPDATE) { + hprinter.printNextItem(pw, rec, baseTime, checkin, + (flags&DUMP_VERBOSE) != 0); + rec.cmd = HistoryItem.CMD_UPDATE; + } + int oldEventCode = rec.eventCode; + HistoryTag oldEventTag = rec.eventTag; rec.eventTag = new HistoryTag(); for (int i=0; i<HistoryItem.EVENT_COUNT; i++) { HashMap<String, SparseIntArray> active @@ -3296,24 +3336,18 @@ public abstract class BatteryStats implements Parcelable { rec.eventTag.string = ent.getKey(); rec.eventTag.uid = uids.keyAt(j); rec.eventTag.poolIdx = uids.valueAt(j); - if (checkin) { - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - } hprinter.printNextItem(pw, rec, baseTime, checkin, (flags&DUMP_VERBOSE) != 0); + rec.wakeReasonTag = null; + rec.wakelockTag = null; } } } - rec.eventCode = oldCode; - rec.eventTag = oldTag; + rec.eventCode = oldEventCode; + rec.eventTag = oldEventTag; tracker = null; } } - if (checkin) { - pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(','); - pw.print(HISTORY_DATA); pw.print(','); - } hprinter.printNextItem(pw, rec, baseTime, checkin, (flags&DUMP_VERBOSE) != 0); } else if (rec.eventCode != HistoryItem.EVENT_NONE) { diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index d71c3e6..1dba77d 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -17,6 +17,7 @@ package android.os; import android.system.ErrnoException; +import android.text.TextUtils; import android.system.Os; import android.system.OsConstants; import android.util.Log; @@ -382,4 +383,32 @@ public class FileUtils { } return filePath.startsWith(dirPath); } + + public static void deleteContents(File dir) { + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteContents(file); + } + file.delete(); + } + } + } + + /** + * Assert that given filename is valid on ext4. + */ + public static boolean isValidExtFilename(String name) { + if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) { + return false; + } + for (int i = 0; i < name.length(); i++) { + final char c = name.charAt(i); + if (c == '\0' || c == '/') { + return false; + } + } + return true; + } } diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index cb3d528..69b828f 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -55,6 +55,14 @@ public abstract class PowerManagerInternal { */ public abstract void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis); + public abstract boolean getLowPowerModeEnabled(); + + public interface LowPowerModeListener { + public void onLowPowerModeChanged(boolean enabled); + } + + public abstract void registerLowPowerModeObserver(LowPowerModeListener listener); + // TODO: Remove this and retrieve as a local service instead. public abstract void setPolicy(WindowManagerPolicy policy); } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 1b3aa0a..112ec1d 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -31,13 +31,17 @@ import java.util.Arrays; import java.util.List; /*package*/ class ZygoteStartFailedEx extends Exception { - /** - * Something prevented the zygote process startup from happening normally - */ + ZygoteStartFailedEx(String s) { + super(s); + } - ZygoteStartFailedEx() {}; - ZygoteStartFailedEx(String s) {super(s);} - ZygoteStartFailedEx(Throwable cause) {super(cause);} + ZygoteStartFailedEx(Throwable cause) { + super(cause); + } + + ZygoteStartFailedEx(String s, Throwable cause) { + super(s, cause); + } } /** @@ -46,9 +50,15 @@ import java.util.List; public class Process { private static final String LOG_TAG = "Process"; - private static final String ZYGOTE_SOCKET = "zygote"; + /** + * @hide for internal use only. + */ + public static final String ZYGOTE_SOCKET = "zygote"; - private static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary"; + /** + * @hide for internal use only. + */ + public static final String SECONDARY_ZYGOTE_SOCKET = "zygote_secondary"; /** * Defines the UID/GID under which system code runs. @@ -338,8 +348,10 @@ public class Process { /** * State for communicating with the zygote process. + * + * @hide for internal use only. */ - static class ZygoteState { + public static class ZygoteState { final LocalSocket socket; final DataInputStream inputStream; final BufferedWriter writer; @@ -355,55 +367,26 @@ public class Process { this.abiList = abiList; } - static ZygoteState connect(String socketAddress, int tries) throws ZygoteStartFailedEx { - LocalSocket zygoteSocket = null; + public static ZygoteState connect(String socketAddress) throws IOException { DataInputStream zygoteInputStream = null; BufferedWriter zygoteWriter = null; + final LocalSocket zygoteSocket = new LocalSocket(); - /* - * See bug #811181: Sometimes runtime can make it up before zygote. - * Really, we'd like to do something better to avoid this condition, - * but for now just wait a bit... - * - * TODO: This bug was filed in 2007. Get rid of this code. The zygote - * forks the system_server so it shouldn't be possible for the zygote - * socket to be brought up after the system_server is. - */ - for (int i = 0; i < tries; i++) { - if (i > 0) { - try { - Log.i(LOG_TAG, "Zygote not up yet, sleeping..."); - Thread.sleep(ZYGOTE_RETRY_MILLIS); - } catch (InterruptedException ex) { - throw new ZygoteStartFailedEx(ex); - } - } + try { + zygoteSocket.connect(new LocalSocketAddress(socketAddress, + LocalSocketAddress.Namespace.RESERVED)); - try { - zygoteSocket = new LocalSocket(); - zygoteSocket.connect(new LocalSocketAddress(socketAddress, - LocalSocketAddress.Namespace.RESERVED)); - - zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); - - zygoteWriter = new BufferedWriter(new OutputStreamWriter( - zygoteSocket.getOutputStream()), 256); - break; - } catch (IOException ex) { - if (zygoteSocket != null) { - try { - zygoteSocket.close(); - } catch (IOException ex2) { - Log.e(LOG_TAG,"I/O exception on close after exception", ex2); - } - } + zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream()); - zygoteSocket = null; + zygoteWriter = new BufferedWriter(new OutputStreamWriter( + zygoteSocket.getOutputStream()), 256); + } catch (IOException ex) { + try { + zygoteSocket.close(); + } catch (IOException ignore) { } - } - if (zygoteSocket == null) { - throw new ZygoteStartFailedEx("connect failed"); + throw ex; } String abiListString = getAbiList(zygoteWriter, zygoteInputStream); @@ -417,7 +400,7 @@ public class Process { return abiList.contains(abi); } - void close() { + public void close() { try { socket.close(); } catch (IOException ex) { @@ -503,27 +486,22 @@ public class Process { * @throws ZygoteStartFailedEx if the query failed. */ private static String getAbiList(BufferedWriter writer, DataInputStream inputStream) - throws ZygoteStartFailedEx { - try { - - // Each query starts with the argument count (1 in this case) - writer.write("1"); - // ... followed by a new-line. - writer.newLine(); - // ... followed by our only argument. - writer.write("--query-abi-list"); - writer.newLine(); - writer.flush(); - - // The response is a length prefixed stream of ASCII bytes. - int numBytes = inputStream.readInt(); - byte[] bytes = new byte[numBytes]; - inputStream.readFully(bytes); - - return new String(bytes, StandardCharsets.US_ASCII); - } catch (IOException ioe) { - throw new ZygoteStartFailedEx(ioe); - } + throws IOException { + // Each query starts with the argument count (1 in this case) + writer.write("1"); + // ... followed by a new-line. + writer.newLine(); + // ... followed by our only argument. + writer.write("--query-abi-list"); + writer.newLine(); + writer.flush(); + + // The response is a length prefixed stream of ASCII bytes. + int numBytes = inputStream.readInt(); + byte[] bytes = new byte[numBytes]; + inputStream.readFully(bytes); + + return new String(bytes, StandardCharsets.US_ASCII); } /** @@ -677,30 +655,16 @@ public class Process { } /** - * Returns the number of times we attempt a connection to the zygote. We - * sleep for {@link #ZYGOTE_RETRY_MILLIS} milliseconds between each try. - * - * This could probably be removed, see TODO in {@code ZygoteState#connect}. - */ - private static int getNumTries(ZygoteState state) { - // Retry 10 times for the first connection to each zygote. - if (state == null) { - return 11; - } - - // This means the connection has already been established, but subsequently - // closed, possibly due to an IOException. We retry just once if that's the - // case. - return 1; - } - - /** * Tries to open socket to Zygote process if not already open. If * already open, does nothing. May block and retry. */ private static ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { if (primaryZygoteState == null || primaryZygoteState.isClosed()) { - primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET, getNumTries(primaryZygoteState)); + try { + primaryZygoteState = ZygoteState.connect(ZYGOTE_SOCKET); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); + } } if (primaryZygoteState.matches(abi)) { @@ -709,8 +673,11 @@ public class Process { // The primary zygote didn't match. Try the secondary. if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { - secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET, - getNumTries(secondaryZygoteState)); + try { + secondaryZygoteState = ZygoteState.connect(SECONDARY_ZYGOTE_SOCKET); + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); + } } if (secondaryZygoteState.matches(abi)) { diff --git a/core/java/android/os/RemoteException.java b/core/java/android/os/RemoteException.java index e30d24f..98d7523 100644 --- a/core/java/android/os/RemoteException.java +++ b/core/java/android/os/RemoteException.java @@ -28,4 +28,9 @@ public class RemoteException extends AndroidException { public RemoteException(String message) { super(message); } + + /** {@hide} */ + public RuntimeException rethrowAsRuntimeException() { + throw new RuntimeException(this); + } } diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java index 29f2545..39cb826 100644 --- a/core/java/android/preference/VolumePreference.java +++ b/core/java/android/preference/VolumePreference.java @@ -66,7 +66,7 @@ public class VolumePreference extends SeekBarDialogPreference implements } public VolumePreference(Context context, AttributeSet attrs) { - this(context, attrs, 0); + this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle); } public void setStreamType(int streamType) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 89f4388..d09bb88 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -769,6 +769,17 @@ public final class Settings { public static final String ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO"; + /** + * Activity Action: Show Device Name Settings. + * <p> + * In some cases, a matching Activity may not exist, so ensure you safeguard + * against ithis. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String DEVICE_NAME_SETTINGS = "android.settings.DEVICE_NAME"; + // End of Intent actions for Settings /** diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index a94f45a..e2e9ff4 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -16,9 +16,11 @@ package android.service.notification; +import android.annotation.PrivateApi; import android.annotation.SdkConstant; import android.app.INotificationManager; import android.app.Service; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.IBinder; @@ -26,9 +28,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; -import java.util.Comparator; -import java.util.HashMap; - /** * A service that receives calls from the system when new notifications are posted or removed. * <p>To extend this class, you must declare the service in your manifest file with @@ -53,6 +52,9 @@ public abstract class NotificationListenerService extends Service { private INotificationManager mNoMan; + /** Only valid after a successful call to (@link registerAsService}. */ + private int mCurrentUser; + /** * The {@link Intent} that must be declared as handled by the service. */ @@ -267,6 +269,42 @@ public abstract class NotificationListenerService extends Service { return true; } + /** + * Directly register this service with the Notification Manager. + * + * <p>Only system services may use this call. It will fail for non-system callers. + * Apps should ask the user to add their listener in Settings. + * + * @param componentName the component that will consume the notification information + * @param currentUser the user to use as the stream filter + * @hide + */ + @PrivateApi + public void registerAsSystemService(ComponentName componentName, int currentUser) + throws RemoteException { + if (mWrapper == null) { + mWrapper = new INotificationListenerWrapper(); + } + INotificationManager noMan = getNotificationInterface(); + noMan.registerListener(mWrapper, componentName, currentUser); + mCurrentUser = currentUser; + } + + /** + * Directly unregister this service from the Notification Manager. + * + * <P>This method will fail for listeners that were not registered + * with (@link registerAsService). + * @hide + */ + @PrivateApi + public void unregisterAsSystemService() throws RemoteException { + if (mWrapper != null) { + INotificationManager noMan = getNotificationInterface(); + noMan.unregisterListener(mWrapper, mCurrentUser); + } + } + private class INotificationListenerWrapper extends INotificationListener.Stub { @Override public void onNotificationPosted(StatusBarNotification sbn, diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl index c346771..9e4c2bf 100644 --- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl +++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl @@ -23,6 +23,6 @@ import android.os.UserHandle; * @hide */ oneway interface ITrustAgentServiceCallback { - void enableTrust(String message, long durationMs, boolean initiatedByUser); + void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser); void revokeTrust(); } diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index d5ce429..bb40eec 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -29,12 +29,12 @@ import android.util.Slog; * to be trusted. * * <p>To extend this class, you must declare the service in your manifest file with - * the {@link android.Manifest.permission#BIND_TRUST_AGENT_SERVICE} permission + * the {@link android.Manifest.permission#BIND_TRUST_AGENT} permission * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> * <pre> * <service android:name=".TrustAgent" * android:label="@string/service_name" - * android:permission="android.permission.BIND_TRUST_AGENT_SERVICE"> + * android:permission="android.permission.BIND_TRUST_AGENT"> * <intent-filter> * <action android:name="android.service.trust.TrustAgentService" /> * </intent-filter> @@ -47,7 +47,7 @@ import android.util.Slog; * {@link android.R.styleable#TrustAgent}. For example:</p> * * <pre> - * <trust_agent xmlns:android="http://schemas.android.com/apk/res/android" + * <trust-agent xmlns:android="http://schemas.android.com/apk/res/android" * android:settingsActivity=".TrustAgentSettings" /></pre> */ public class TrustAgentService extends Service { @@ -88,7 +88,7 @@ public class TrustAgentService extends Service { * * @param successful true if the attempt succeeded */ - protected void onUnlockAttempt(boolean successful) { + public void onUnlockAttempt(boolean successful) { } private void onError(String msg) { @@ -96,7 +96,7 @@ public class TrustAgentService extends Service { } /** - * Call to enable trust on the device. + * Call to grant trust on the device. * * @param message describes why the device is trusted, e.g. "Trusted by location". * @param durationMs amount of time in milliseconds to keep the device in a trusted state. Trust @@ -104,10 +104,10 @@ public class TrustAgentService extends Service { * @param initiatedByUser indicates that the user has explicitly initiated an action that proves * the user is about to use the device. */ - protected final void enableTrust(String message, long durationMs, boolean initiatedByUser) { + public final void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser) { if (mCallback != null) { try { - mCallback.enableTrust(message, durationMs, initiatedByUser); + mCallback.grantTrust(message.toString(), durationMs, initiatedByUser); } catch (RemoteException e) { onError("calling enableTrust()"); } @@ -117,7 +117,7 @@ public class TrustAgentService extends Service { /** * Call to revoke trust on the device. */ - protected final void revokeTrust() { + public final void revokeTrust() { if (mCallback != null) { try { mCallback.revokeTrust(); diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 5a432dc..9a70099 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -24,7 +24,6 @@ import android.util.ArrayMap; import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; -import android.util.SparseIntArray; import android.util.SparseLongArray; import android.view.SurfaceView; import android.view.TextureView; @@ -108,6 +107,40 @@ public abstract class Transition implements Cloneable { private static final String LOG_TAG = "Transition"; static final boolean DBG = false; + /** + * With {@link #setMatchOrder(int...)}, chooses to match by View instance. + */ + public static final int MATCH_INSTANCE = 0x1; + private static final int MATCH_FIRST = MATCH_INSTANCE; + + /** + * With {@link #setMatchOrder(int...)}, chooses to match by + * {@link android.view.View#getViewName()}. Null names will not be matched. + */ + public static final int MATCH_VIEW_NAME = 0x2; + + /** + * With {@link #setMatchOrder(int...)}, chooses to match by + * {@link android.view.View#getId()}. Negative IDs will not be matched. + */ + public static final int MATCH_ID = 0x3; + + /** + * With {@link #setMatchOrder(int...)}, chooses to match by the {@link android.widget.Adapter} + * item id. When {@link android.widget.Adapter#hasStableIds()} returns false, no match + * will be made for items. + */ + public static final int MATCH_ITEM_ID = 0x4; + + private static final int MATCH_LAST = MATCH_ITEM_ID; + + private static final int[] DEFAULT_MATCH_ORDER = { + MATCH_VIEW_NAME, + MATCH_INSTANCE, + MATCH_ID, + MATCH_ITEM_ID, + }; + private String mName = getClass().getName(); long mStartDelay = -1; @@ -127,6 +160,7 @@ public abstract class Transition implements Cloneable { private TransitionValuesMaps mStartValues = new TransitionValuesMaps(); private TransitionValuesMaps mEndValues = new TransitionValuesMaps(); TransitionSet mParent = null; + private int[] mMatchOrder = DEFAULT_MATCH_ORDER; // Per-animator information used for later canceling when future transitions overlap private static ThreadLocal<ArrayMap<Animator, AnimationInfo>> sRunningAnimators = @@ -338,6 +372,53 @@ public abstract class Transition implements Cloneable { } /** + * Sets the order in which Transition matches View start and end values. + * <p> + * The default behavior is to match first by {@link android.view.View#getViewName()}, + * then by View instance, then by {@link android.view.View#getId()} and finally + * by its item ID if it is in a direct child of ListView. The caller can + * choose to have only some or all of the values of {@link #MATCH_INSTANCE}, + * {@link #MATCH_VIEW_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}. Only + * the match algorithms supplied will be used to determine whether Views are the + * the same in both the start and end Scene. Views that do not match will be considered + * as entering or leaving the Scene. + * </p> + * @param matches A list of zero or more of {@link #MATCH_INSTANCE}, + * {@link #MATCH_VIEW_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}. + * If none are provided, then the default match order will be set. + */ + public void setMatchOrder(int... matches) { + if (matches == null || matches.length == 0) { + mMatchOrder = DEFAULT_MATCH_ORDER; + } else { + for (int i = 0; i < matches.length; i++) { + int match = matches[i]; + if (!isValidMatch(match)) { + throw new IllegalArgumentException("matches contains invalid value"); + } + if (alreadyContains(matches, i)) { + throw new IllegalArgumentException("matches contains a duplicate value"); + } + } + mMatchOrder = matches.clone(); + } + } + + private static boolean isValidMatch(int match) { + return (match >= MATCH_FIRST && match <= MATCH_LAST); + } + + private static boolean alreadyContains(int[] array, int searchIndex) { + int value = array[searchIndex]; + for (int i = 0; i < searchIndex; i++) { + if (array[i] == value) { + return true; + } + } + return false; + } + + /** * Match start/end values by View instance. Adds matched values to startValuesList * and endValuesList and removes them from unmatchedStart and unmatchedEnd. */ @@ -464,6 +545,37 @@ public abstract class Transition implements Cloneable { } } + private void matchStartAndEnd(TransitionValuesMaps startValues, + TransitionValuesMaps endValues, + ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList) { + ArrayMap<View, TransitionValues> unmatchedStart = + new ArrayMap<View, TransitionValues>(startValues.viewValues); + ArrayMap<View, TransitionValues> unmatchedEnd = + new ArrayMap<View, TransitionValues>(endValues.viewValues); + + for (int i = 0; i < mMatchOrder.length; i++) { + switch (mMatchOrder[i]) { + case MATCH_INSTANCE: + matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd); + break; + case MATCH_VIEW_NAME: + matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.nameValues, endValues.nameValues); + break; + case MATCH_ID: + matchIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.idValues, endValues.idValues); + break; + case MATCH_ITEM_ID: + matchItemIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.itemIdValues, endValues.itemIdValues); + break; + } + } + addUnmatched(startValuesList, endValuesList, unmatchedStart, unmatchedEnd); + } + /** * This method, essentially a wrapper around all calls to createAnimator for all * possible target views, is called with the entire set of start/end @@ -480,21 +592,9 @@ public abstract class Transition implements Cloneable { if (DBG) { Log.d(LOG_TAG, "createAnimators() for " + this); } - ArrayMap<View, TransitionValues> unmatchedStart = - new ArrayMap<View, TransitionValues>(startValues.viewValues); - ArrayMap<View, TransitionValues> unmatchedEnd = - new ArrayMap<View, TransitionValues>(endValues.viewValues); - ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>(); ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>(); - matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, - startValues.nameValues, endValues.nameValues); - matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd); - matchIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, - startValues.idValues, endValues.idValues); - matchItemIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, - startValues.itemIdValues, endValues.itemIdValues); - addUnmatched(startValuesList, endValuesList, unmatchedStart, unmatchedEnd); + matchStartAndEnd(startValues, endValues, startValuesList, endValuesList); ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators(); long minStartDelay = Long.MAX_VALUE; diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java index 04f8672..f4b562f 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -30,6 +30,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; +import java.util.StringTokenizer; /** * This class inflates scenes and transitions from resource files. @@ -40,6 +41,10 @@ import java.util.ArrayList; * and {@link android.R.styleable#TransitionManager}. */ public class TransitionInflater { + private static final String MATCH_INSTANCE = "instance"; + private static final String MATCH_VIEW_NAME = "viewName"; + private static final String MATCH_ID = "id"; + private static final String MATCH_ITEM_ID = "itemId"; private Context mContext; @@ -266,6 +271,33 @@ public class TransitionInflater { } } + private int[] parseMatchOrder(String matchOrderString) { + StringTokenizer st = new StringTokenizer(matchOrderString, ","); + int matches[] = new int[st.countTokens()]; + int index = 0; + while (st.hasMoreTokens()) { + String token = st.nextToken().trim(); + if (MATCH_ID.equalsIgnoreCase(token)) { + matches[index] = Transition.MATCH_ID; + } else if (MATCH_INSTANCE.equalsIgnoreCase(token)) { + matches[index] = Transition.MATCH_INSTANCE; + } else if (MATCH_VIEW_NAME.equalsIgnoreCase(token)) { + matches[index] = Transition.MATCH_VIEW_NAME; + } else if (MATCH_ITEM_ID.equalsIgnoreCase(token)) { + matches[index] = Transition.MATCH_ITEM_ID; + } else if (token.isEmpty()) { + int[] smallerMatches = new int[matches.length - 1]; + System.arraycopy(matches, 0, smallerMatches, 0, index); + matches = smallerMatches; + index--; + } else { + throw new RuntimeException("Unknown match type in matchOrder: '" + token + "'"); + } + index++; + } + return matches; + } + private Transition loadTransition(Transition transition, AttributeSet attrs) throws Resources.NotFoundException { @@ -284,6 +316,11 @@ public class TransitionInflater { if (resID > 0) { transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID)); } + String matchOrder = + a.getString(com.android.internal.R.styleable.Transition_matchOrder); + if (matchOrder != null) { + transition.setMatchOrder(parseMatchOrder(matchOrder)); + } a.recycle(); return transition; } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 6c451eb..a272296 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -1127,14 +1127,15 @@ class GLES20Canvas extends HardwareCanvas { int modifiers = setupModifiers(paint); try { - nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint); + nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint, + paint.mNativeTypeface); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } } private static native void nDrawText(long renderer, char[] text, int index, int count, - float x, float y, int bidiFlags, long paint); + float x, float y, int bidiFlags, long paint, long typeface); @Override public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) { @@ -1143,7 +1144,7 @@ class GLES20Canvas extends HardwareCanvas { if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags, - paint.mNativePaint); + paint.mNativePaint, paint.mNativeTypeface); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawText(this, start, end, x, y, paint); @@ -1151,7 +1152,7 @@ class GLES20Canvas extends HardwareCanvas { char[] buf = TemporaryBuffer.obtain(end - start); TextUtils.getChars(text, start, end, buf, 0); nDrawText(mRenderer, buf, 0, end - start, x, y, - paint.mBidiFlags, paint.mNativePaint); + paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface); TemporaryBuffer.recycle(buf); } } finally { @@ -1167,21 +1168,22 @@ class GLES20Canvas extends HardwareCanvas { int modifiers = setupModifiers(paint); try { - nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint); + nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint, + paint.mNativeTypeface); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } } private static native void nDrawText(long renderer, String text, int start, int end, - float x, float y, int bidiFlags, long paint); + float x, float y, int bidiFlags, long paint, long typeface); @Override public void drawText(String text, float x, float y, Paint paint) { int modifiers = setupModifiers(paint); try { nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags, - paint.mNativePaint); + paint.mNativePaint, paint.mNativeTypeface); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } @@ -1235,14 +1237,14 @@ class GLES20Canvas extends HardwareCanvas { int modifiers = setupModifiers(paint); try { nDrawTextRun(mRenderer, text, index, count, contextIndex, contextCount, x, y, dir, - paint.mNativePaint); + paint.mNativePaint, paint.mNativeTypeface); } finally { if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } } private static native void nDrawTextRun(long renderer, char[] text, int index, int count, - int contextIndex, int contextCount, float x, float y, int dir, long nativePaint); + int contextIndex, int contextCount, float x, float y, int dir, long nativePaint, long nativeTypeface); @Override public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, @@ -1257,7 +1259,7 @@ class GLES20Canvas extends HardwareCanvas { if (text instanceof String || text instanceof SpannedString || text instanceof SpannableString) { nDrawTextRun(mRenderer, text.toString(), start, end, contextStart, - contextEnd, x, y, flags, paint.mNativePaint); + contextEnd, x, y, flags, paint.mNativePaint, paint.mNativeTypeface); } else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawTextRun(this, start, end, contextStart, contextEnd, x, y, flags, paint); @@ -1267,7 +1269,7 @@ class GLES20Canvas extends HardwareCanvas { char[] buf = TemporaryBuffer.obtain(contextLen); TextUtils.getChars(text, contextStart, contextEnd, buf, 0); nDrawTextRun(mRenderer, buf, start - contextStart, len, 0, contextLen, - x, y, flags, paint.mNativePaint); + x, y, flags, paint.mNativePaint, paint.mNativeTypeface); TemporaryBuffer.recycle(buf); } } finally { @@ -1276,7 +1278,7 @@ class GLES20Canvas extends HardwareCanvas { } private static native void nDrawTextRun(long renderer, String text, int start, int end, - int contextStart, int contextEnd, float x, float y, int flags, long nativePaint); + int contextStart, int contextEnd, float x, float y, int flags, long nativePaint, long nativeTypeface); @Override public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset, diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java index 7b49006..9601a8d 100644 --- a/core/java/android/view/GLRenderer.java +++ b/core/java/android/view/GLRenderer.java @@ -1220,10 +1220,6 @@ public class GLRenderer extends HardwareRenderer { } private RenderNode buildDisplayList(View view, HardwareCanvas canvas) { - if (mDrawDelta <= 0) { - return view.mRenderNode; - } - view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 0cfde94..b2839cb 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -387,6 +387,10 @@ public class RenderNode { nSetClipToOutline(mNativeRenderNode, clipToOutline); } + public boolean getClipToOutline() { + return nGetClipToOutline(mNativeRenderNode); + } + /** * Controls the RenderNode's circular reveal clip. */ @@ -919,6 +923,7 @@ public class RenderNode { private static native void nSetAnimationMatrix(long renderNode, long animationMatrix); private static native boolean nHasOverlappingRendering(long renderNode); + private static native boolean nGetClipToOutline(long renderNode); private static native float nGetAlpha(long renderNode); private static native float nGetLeft(long renderNode); private static native float nGetTop(long renderNode); diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index ec4d560..f14e73f 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -21,7 +21,6 @@ import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; import android.util.SparseIntArray; -import android.util.TimeUtils; import com.android.internal.util.VirtualRefBasePtr; import com.android.internal.view.animation.FallbackLUTInterpolator; @@ -72,10 +71,6 @@ public final class RenderNodeAnimator { put(ViewPropertyAnimator.ALPHA, ALPHA); }}; - // Keep in sync DeltaValueType in Animator.h - public static final int DELTA_TYPE_ABSOLUTE = 0; - public static final int DELTA_TYPE_DELTA = 1; - private VirtualRefBasePtr mNativePtr; private RenderNode mTarget; @@ -86,22 +81,21 @@ public final class RenderNodeAnimator { return sViewPropertyAnimatorMap.get(viewProperty); } - public RenderNodeAnimator(int property, int deltaType, float deltaValue) { + public RenderNodeAnimator(int property, float finalValue) { init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), - property, deltaType, deltaValue)); + property, finalValue)); } - public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) { + public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) { init(nCreateCanvasPropertyFloatAnimator( new WeakReference<RenderNodeAnimator>(this), - property.getNativeContainer(), deltaType, deltaValue)); + property.getNativeContainer(), finalValue)); } - public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, - int deltaType, float deltaValue) { + public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) { init(nCreateCanvasPropertyPaintAnimator( new WeakReference<RenderNodeAnimator>(this), - property.getNativeContainer(), paintField, deltaType, deltaValue)); + property.getNativeContainer(), paintField, finalValue)); } private void init(long ptr) { @@ -182,11 +176,11 @@ public final class RenderNodeAnimator { } private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis, - int property, int deltaValueType, float deltaValue); + int property, float deltaValue); private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis, - long canvasProperty, int deltaValueType, float deltaValue); + long canvasProperty, float deltaValue); private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis, - long canvasProperty, int paintField, int deltaValueType, float deltaValue); + long canvasProperty, int paintField, float deltaValue); private static native void nSetDuration(long nativePtr, int duration); private static native int nGetDuration(long nativePtr); private static native void nSetInterpolator(long animPtr, long interpolatorPtr); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e829141..5141877 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2774,8 +2774,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * @hide + * + * Makes system ui transparent. + */ + public static final int SYSTEM_UI_TRANSPARENT = 0x00008000; + + /** + * @hide */ - public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x0000FFFF; + public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00007FFF; /** * These are the system UI flags that can be cleared by events outside @@ -7147,6 +7154,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (viewRootImpl != null) { viewRootImpl.setAccessibilityFocus(this, null); } + Rect rect = (mAttachInfo != null) ? mAttachInfo.mTmpInvalRect : new Rect(); + getDrawingRect(rect); + requestRectangleOnScreen(rect, false); invalidate(); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); return true; @@ -10696,9 +10706,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mRenderNode.setOutline(mOutline); } - // TODO: remove - public final boolean getClipToOutline() { return false; } - public void setClipToOutline(boolean clipToOutline) {} + public final boolean getClipToOutline() { + return mRenderNode.getClipToOutline(); + } + + public void setClipToOutline(boolean clipToOutline) { + // TODO: add a fast invalidation here + if (getClipToOutline() != clipToOutline) { + mRenderNode.setClipToOutline(clipToOutline); + } + } private void queryOutlineFromBackgroundIfUndefined() { if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) { @@ -10707,7 +10724,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mOutline = new Outline(); } else { //invalidate outline, to ensure background calculates it - mOutline.set(null); + mOutline.reset(); } if (mBackground.getOutline(mOutline)) { if (!mOutline.isValid()) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 92a42b7..b821a3e 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2318,8 +2318,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * in Activity transitions. If false, the ViewGroup won't transition, * only its children. If true, the entire ViewGroup will transition * together. - * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window, - * android.app.ActivityOptions.ActivityTransitionListener) + * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity, + * android.util.Pair[]) */ public void setTransitionGroup(boolean isTransitionGroup) { mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 375f5e3..ecc4586 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -642,9 +642,7 @@ public abstract class Window { final WindowManager.LayoutParams attrs = getAttributes(); attrs.width = width; attrs.height = height; - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } /** @@ -661,9 +659,7 @@ public abstract class Window { { final WindowManager.LayoutParams attrs = getAttributes(); attrs.gravity = gravity; - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } /** @@ -675,9 +671,7 @@ public abstract class Window { public void setType(int type) { final WindowManager.LayoutParams attrs = getAttributes(); attrs.type = type; - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } /** @@ -700,9 +694,7 @@ public abstract class Window { attrs.format = mDefaultWindowFormat; mHaveWindowFormat = false; } - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } /** @@ -715,9 +707,7 @@ public abstract class Window { public void setWindowAnimations(int resId) { final WindowManager.LayoutParams attrs = getAttributes(); attrs.windowAnimations = resId; - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } /** @@ -735,9 +725,7 @@ public abstract class Window { } else { mHasSoftInputMode = false; } - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } /** @@ -793,14 +781,19 @@ public abstract class Window { attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY; } mForcedWindowFlags |= mask; - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } private void setPrivateFlags(int flags, int mask) { final WindowManager.LayoutParams attrs = getAttributes(); attrs.privateFlags = (attrs.privateFlags & ~mask) | (flags & mask); + dispatchWindowAttributesChanged(attrs); + } + + /** + * {@hide} + */ + protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) { if (mCallback != null) { mCallback.onWindowAttributesChanged(attrs); } @@ -818,9 +811,7 @@ public abstract class Window { final WindowManager.LayoutParams attrs = getAttributes(); attrs.dimAmount = amount; mHaveDimAmount = true; - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } /** @@ -835,9 +826,7 @@ public abstract class Window { */ public void setAttributes(WindowManager.LayoutParams a) { mWindowAttributes.copyFrom(a); - if (mCallback != null) { - mCallback.onWindowAttributesChanged(mWindowAttributes); - } + dispatchWindowAttributesChanged(mWindowAttributes); } /** @@ -1269,9 +1258,7 @@ public abstract class Window { if (!mHaveWindowFormat) { final WindowManager.LayoutParams attrs = getAttributes(); attrs.format = format; - if (mCallback != null) { - mCallback.onWindowAttributesChanged(attrs); - } + dispatchWindowAttributesChanged(attrs); } } @@ -1527,4 +1514,44 @@ public abstract class Window { * until the called Activity's exiting transition completes. */ public boolean getAllowExitTransitionOverlap() { return true; } + + /** + * @return the color of the status bar. + */ + public abstract int getStatusBarColor(); + + /** + * Sets the color of the status bar to {@param color}. + * + * For this to take effect, + * the window must be drawing the system bar backgrounds with + * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and + * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set. + * + * If {@param color} is not opaque, consider setting + * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and + * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}. + */ + public abstract void setStatusBarColor(int color); + + /** + * @return the color of the navigation bar. + */ + public abstract int getNavigationBarColor(); + + /** + * Sets the color of the navigation bar to {@param color}. + * + * For this to take effect, + * the window must be drawing the system bar backgrounds with + * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and + * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION} must not be set. + * + * If {@param color} is not opaque, consider setting + * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and + * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}. + */ + public abstract void setNavigationBarColor(int color); + + } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 032a82f..031ad80 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -915,6 +915,14 @@ public interface WindowManager extends ViewManager { public static final int FLAG_NEEDS_MENU_KEY = 0x40000000; /** + * Flag indicating that this Window is responsible for drawing the background for the + * system bars. If set, the system bars are drawn with a transparent background and the + * corresponding areas in this window are filled with the colors specified in + * {@link Window#getStatusBarColor()} and {@link Window#getNavigationBarColor()}. + */ + public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000; + + /** * Various behavioral options/flags. Default is none. * * @see #FLAG_ALLOW_LOCK_WHILE_SCREEN_ON @@ -941,6 +949,7 @@ public interface WindowManager extends ViewManager { * @see #FLAG_SPLIT_TOUCH * @see #FLAG_HARDWARE_ACCELERATED * @see #FLAG_LOCAL_FOCUS_MODE + * @see #FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS */ @ViewDebug.ExportedProperty(flagMapping = { @ViewDebug.FlagToString(mask = FLAG_ALLOW_LOCK_WHILE_SCREEN_ON, equals = FLAG_ALLOW_LOCK_WHILE_SCREEN_ON, @@ -998,7 +1007,9 @@ public interface WindowManager extends ViewManager { @ViewDebug.FlagToString(mask = FLAG_TRANSLUCENT_STATUS, equals = FLAG_TRANSLUCENT_STATUS, name = "FLAG_TRANSLUCENT_STATUS"), @ViewDebug.FlagToString(mask = FLAG_TRANSLUCENT_NAVIGATION, equals = FLAG_TRANSLUCENT_NAVIGATION, - name = "FLAG_TRANSLUCENT_NAVIGATION") + name = "FLAG_TRANSLUCENT_NAVIGATION"), + @ViewDebug.FlagToString(mask = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, equals = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + name = "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS") }) public int flags; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 9d10930..34967df 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -17,15 +17,19 @@ package android.view.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; +import android.annotation.Nullable; import android.graphics.Rect; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.InputType; +import android.text.TextUtils; +import android.util.ArraySet; import android.util.LongArray; import android.util.Pools.SynchronizedPool; import android.view.View; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -112,7 +116,7 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ACTION_SELECT = 0x00000004; /** - * Action that unselects the node. + * Action that deselects the node. */ public static final int ACTION_CLEAR_SELECTION = 0x00000008; @@ -307,6 +311,18 @@ public class AccessibilityNodeInfo implements Parcelable { */ public static final int ACTION_SET_TEXT = 0x00200000; + private static final int LAST_LEGACY_STANDARD_ACTION = ACTION_SET_TEXT; + + /** + * Mask to see if the value is larger than the largest ACTION_ constant + */ + private static final int ACTION_TYPE_MASK = 0xFF000000; + + /** + * Mask to define standard not legacy actions. + */ + private static final int STANDARD_NON_LEGACY_ACTION_MASK = 0x01000000; + // Action arguments /** @@ -548,7 +564,7 @@ public class AccessibilityNodeInfo implements Parcelable { private String mViewIdResourceName; private LongArray mChildNodeIds; - private int mActions; + private ArrayList<AccessibilityAction> mActions; private int mMovementGranularities; @@ -875,6 +891,17 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Gets the actions that can be performed on the node. + */ + public List<AccessibilityAction> getActionList() { + if (mActions == null) { + return Collections.emptyList(); + } + + return mActions; + } + + /** + * Gets the actions that can be performed on the node. * * @return The bit mask of with actions. * @@ -892,9 +919,61 @@ public class AccessibilityNodeInfo implements Parcelable { * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD + * + * @deprecated Use {@link #getActionList()}. */ + @Deprecated public int getActions() { - return mActions; + int returnValue = 0; + + if (mActions == null) { + return returnValue; + } + + final int actionSize = mActions.size(); + for (int i = 0; i < actionSize; i++) { + int actionId = mActions.get(i).getId(); + if (actionId <= LAST_LEGACY_STANDARD_ACTION) { + returnValue |= actionId; + } + } + + return returnValue; + } + + /** + * Adds an action that can be performed on the node. + * <p> + * To add a standard action use the static constants on {@link AccessibilityAction}. + * To add a custom action create a new {@link AccessibilityAction} by passing in a + * resource id from your application as the action id and an optional label that + * describes the action. To override one of the standard actions use as the action + * id of a standard action id such as {@link #ACTION_CLICK} and an optional label that + * describes the action. + * </p> + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param action The action. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public void addAction(AccessibilityAction action) { + enforceNotSealed(); + + if (action == null) { + return; + } + + if (mActions == null) { + mActions = new ArrayList<AccessibilityAction>(); + } + + mActions.remove(action); + mActions.add(action); } /** @@ -908,10 +987,21 @@ public class AccessibilityNodeInfo implements Parcelable { * @param action The action. * * @throws IllegalStateException If called from an AccessibilityService. + * @throws IllegalArgumentException If the argument is not one of the standard actions. + * + * @deprecated This has been deprecated for {@link #addAction(AccessibilityAction)} */ + @Deprecated public void addAction(int action) { enforceNotSealed(); - mActions |= action; + + AccessibilityAction newAction = getActionSingleton(action); + if (newAction == null) { + // This means it is not one of the standard actions + throw new IllegalArgumentException("Argument is not one of the standard actions"); + } + + addAction(newAction); } /** @@ -923,13 +1013,40 @@ public class AccessibilityNodeInfo implements Parcelable { * This class is made immutable before being delivered to an AccessibilityService. * </p> * - * @param action The action. + * @param action The action to be removed. * * @throws IllegalStateException If called from an AccessibilityService. + * @deprecated Use {@link #removeAction(AccessibilityAction)} */ + @Deprecated public void removeAction(int action) { enforceNotSealed(); - mActions &= ~action; + + removeAction(getActionSingleton(action)); + } + + /** + * Removes an action that can be performed on the node. If the action was + * not already added to the node, calling this method has no effect. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> + * + * @param action The action to be removed. + * @return The action removed from the list of actions. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + public boolean removeAction(AccessibilityAction action) { + enforceNotSealed(); + + if (mActions == null || action == null) { + return false; + } + + return mActions.remove(action); } /** @@ -2307,7 +2424,29 @@ public class AccessibilityNodeInfo implements Parcelable { parcel.writeInt(mBoundsInScreen.left); parcel.writeInt(mBoundsInScreen.right); - parcel.writeInt(mActions); + if (mActions != null && !mActions.isEmpty()) { + final int actionCount = mActions.size(); + parcel.writeInt(actionCount); + + int defaultLegacyStandardActions = 0; + for (int i = 0; i < actionCount; i++) { + AccessibilityAction action = mActions.get(i); + if (isDefaultLegacyStandardAction(action)) { + defaultLegacyStandardActions |= action.getId(); + } + } + parcel.writeInt(defaultLegacyStandardActions); + + for (int i = 0; i < actionCount; i++) { + AccessibilityAction action = mActions.get(i); + if (!isDefaultLegacyStandardAction(action)) { + parcel.writeInt(action.getId()); + parcel.writeCharSequence(action.getLabel()); + } + } + } else { + parcel.writeInt(0); + } parcel.writeInt(mMovementGranularities); @@ -2388,7 +2527,17 @@ public class AccessibilityNodeInfo implements Parcelable { mText = other.mText; mContentDescription = other.mContentDescription; mViewIdResourceName = other.mViewIdResourceName; - mActions= other.mActions; + + final ArrayList<AccessibilityAction> otherActions = other.mActions; + if (otherActions != null && otherActions.size() > 0) { + if (mActions == null) { + mActions = new ArrayList(otherActions); + } else { + mActions.clear(); + mActions.addAll(other.mActions); + } + } + mBooleanProperties = other.mBooleanProperties; mMovementGranularities = other.mMovementGranularities; @@ -2452,7 +2601,17 @@ public class AccessibilityNodeInfo implements Parcelable { mBoundsInScreen.left = parcel.readInt(); mBoundsInScreen.right = parcel.readInt(); - mActions = parcel.readInt(); + final int actionCount = parcel.readInt(); + if (actionCount > 0) { + final int legacyStandardActions = parcel.readInt(); + addLegacyStandardActions(legacyStandardActions); + final int nonLegacyActionCount = actionCount - Integer.bitCount(legacyStandardActions); + for (int i = 0; i < nonLegacyActionCount; i++) { + AccessibilityAction action = new AccessibilityAction( + parcel.readInt(), parcel.readCharSequence()); + addAction(action); + } + } mMovementGranularities = parcel.readInt(); @@ -2524,7 +2683,9 @@ public class AccessibilityNodeInfo implements Parcelable { mText = null; mContentDescription = null; mViewIdResourceName = null; - mActions = 0; + if (mActions != null) { + mActions.clear(); + } mTextSelectionStart = UNDEFINED_SELECTION_INDEX; mTextSelectionEnd = UNDEFINED_SELECTION_INDEX; mInputType = InputType.TYPE_NULL; @@ -2546,6 +2707,33 @@ public class AccessibilityNodeInfo implements Parcelable { } } + private static boolean isDefaultLegacyStandardAction(AccessibilityAction action) { + return (action.getId() <= LAST_LEGACY_STANDARD_ACTION + && TextUtils.isEmpty(action.getLabel())); + } + + private static AccessibilityAction getActionSingleton(int actionId) { + final int actions = AccessibilityAction.sStandardActions.size(); + for (int i = 0; i < actions; i++) { + AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i); + if (actionId == currentAction.getId()) { + return currentAction; + } + } + + return null; + } + + private void addLegacyStandardActions(int actionMask) { + int remainingIds = actionMask; + while (remainingIds > 0) { + final int id = 1 << Integer.numberOfTrailingZeros(remainingIds); + remainingIds &= ~id; + AccessibilityAction action = getActionSingleton(id); + addAction(action); + } + } + /** * Gets the human readable action symbolic name. * @@ -2709,20 +2897,429 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; longClickable: ").append(isLongClickable()); builder.append("; enabled: ").append(isEnabled()); builder.append("; password: ").append(isPassword()); - builder.append("; scrollable: " + isScrollable()); - - builder.append("; ["); - for (int actionBits = mActions; actionBits != 0;) { - final int action = 1 << Integer.numberOfTrailingZeros(actionBits); - actionBits &= ~action; - builder.append(getActionSymbolicName(action)); - if (actionBits != 0) { - builder.append(", "); + builder.append("; scrollable: ").append(isScrollable()); + builder.append("; actions: ").append(mActions); + + return builder.toString(); + } + + /** + * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}. + * Each action has a unique id that is mandatory and optional data. + * <p> + * There are three categories of actions: + * <ul> + * <li><strong>Standard actions</strong> - These are actions that are reported and + * handled by the standard UI widgets in the platform. For each standard action + * there is a static constant defined in this class, e.g. {@link #ACTION_FOCUS}. + * </li> + * <li><strong>Custom actions action</strong> - These are actions that are reported + * and handled by custom widgets. i.e. ones that are not part of the UI toolkit. For + * example, an application may define a custom action for clearing the user history. + * </li> + * <li><strong>Overriden standard actions</strong> - These are actions that override + * standard actions to customize them. For example, an app may add a label to the + * standard click action to announce that this action clears browsing history. + * </ul> + * </p> + */ + public static final class AccessibilityAction { + + /** + * Action that gives input focus to the node. + */ + public static final AccessibilityAction ACTION_FOCUS = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_FOCUS, null); + + /** + * Action that clears input focus of the node. + */ + public static final AccessibilityAction ACTION_CLEAR_FOCUS = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLEAR_FOCUS, null); + + /** + * Action that selects the node. + */ + public static final AccessibilityAction ACTION_SELECT = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_SELECT, null); + + /** + * Action that deselects the node. + */ + public static final AccessibilityAction ACTION_CLEAR_SELECTION = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLEAR_SELECTION, null); + + /** + * Action that clicks on the node info. + */ + public static final AccessibilityAction ACTION_CLICK = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLICK, null); + + /** + * Action that long clicks on the node. + */ + public static final AccessibilityAction ACTION_LONG_CLICK = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_LONG_CLICK, null); + + /** + * Action that gives accessibility focus to the node. + */ + public static final AccessibilityAction ACTION_ACCESSIBILITY_FOCUS = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + + /** + * Action that clears accessibility focus of the node. + */ + public static final AccessibilityAction ACTION_CLEAR_ACCESSIBILITY_FOCUS = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); + + /** + * Action that requests to go to the next entity in this node's text + * at a given movement granularity. For example, move to the next character, + * word, etc. + * <p> + * <strong>Arguments:</strong> + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}, + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> + * <strong>Example:</strong> Move to the previous character and do not extend selection. + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); + * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, + * false); + * info.performAction(AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(), + * arguments); + * </code></pre></p> + * </p> + * + * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * + * @see AccessibilityNodeInfo#setMovementGranularities(int) + * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * @see AccessibilityNodeInfo#getMovementGranularities() + * AccessibilityNodeInfo.getMovementGranularities() + * + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE + */ + public static final AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null); + + /** + * Action that requests to go to the previous entity in this node's text + * at a given movement granularity. For example, move to the next character, + * word, etc. + * <p> + * <strong>Arguments:</strong> + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}, + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br> + * <strong>Example:</strong> Move to the next character and do not extend selection. + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER); + * arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, + * false); + * info.performAction(AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(), + * arguments); + * </code></pre></p> + * </p> + * + * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + * AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT + * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN + * + * @see AccessibilityNodeInfo#setMovementGranularities(int) + * AccessibilityNodeInfo.setMovementGranularities(int) + * @see AccessibilityNodeInfo#getMovementGranularities() + * AccessibilityNodeInfo.getMovementGranularities() + * + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH + * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE + * AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE + */ + public static final AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null); + + /** + * Action to move to the next HTML element of a given type. For example, move + * to the BUTTON, INPUT, TABLE, etc. + * <p> + * <strong>Arguments:</strong> + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING + * AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> + * <strong>Example:</strong> + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); + * info.performAction(AccessibilityAction.ACTION_NEXT_HTML_ELEMENT.getId(), arguments); + * </code></pre></p> + * </p> + */ + public static final AccessibilityAction ACTION_NEXT_HTML_ELEMENT = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, null); + + /** + * Action to move to the previous HTML element of a given type. For example, move + * to the BUTTON, INPUT, TABLE, etc. + * <p> + * <strong>Arguments:</strong> + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING + * AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br> + * <strong>Example:</strong> + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON"); + * info.performAction(AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments); + * </code></pre></p> + * </p> + */ + public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, null); + + /** + * Action to scroll the node content forward. + */ + public static final AccessibilityAction ACTION_SCROLL_FORWARD = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null); + + /** + * Action to scroll the node content backward. + */ + public static final AccessibilityAction ACTION_SCROLL_BACKWARD = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null); + + /** + * Action to copy the current selection to the clipboard. + */ + public static final AccessibilityAction ACTION_COPY = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_COPY, null); + + /** + * Action to paste the current clipboard content. + */ + public static final AccessibilityAction ACTION_PASTE = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_PASTE, null); + + /** + * Action to cut the current selection and place it to the clipboard. + */ + public static final AccessibilityAction ACTION_CUT = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_CUT, null); + + /** + * Action to set the selection. Performing this action with no arguments + * clears the selection. + * <p> + * <strong>Arguments:</strong> + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT + * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT}, + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT + * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT}<br> + * <strong>Example:</strong> + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1); + * arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2); + * info.performAction(AccessibilityAction.ACTION_SET_SELECTION.getId(), arguments); + * </code></pre></p> + * </p> + * + * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT + * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT + * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT + * AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT + */ + public static final AccessibilityAction ACTION_SET_SELECTION = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_SET_SELECTION, null); + + /** + * Action to expand an expandable node. + */ + public static final AccessibilityAction ACTION_EXPAND = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_EXPAND, null); + + /** + * Action to collapse an expandable node. + */ + public static final AccessibilityAction ACTION_COLLAPSE = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_COLLAPSE, null); + + /** + * Action to dismiss a dismissable node. + */ + public static final AccessibilityAction ACTION_DISMISS = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_DISMISS, null); + + /** + * Action that sets the text of the node. Performing the action without argument, + * using <code> null</code> or empty {@link CharSequence} will clear the text. This + * action will also put the cursor at the end of text. + * <p> + * <strong>Arguments:</strong> + * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE + * AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br> + * <strong>Example:</strong> + * <code><pre><p> + * Bundle arguments = new Bundle(); + * arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, + * "android"); + * info.performAction(AccessibilityAction.ACTION_SET_TEXT.getId(), arguments); + * </code></pre></p> + */ + public static final AccessibilityAction ACTION_SET_TEXT = + new AccessibilityAction( + AccessibilityNodeInfo.ACTION_SET_TEXT, null); + + private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<AccessibilityAction>(); + static { + sStandardActions.add(ACTION_FOCUS); + sStandardActions.add(ACTION_CLEAR_FOCUS); + sStandardActions.add(ACTION_SELECT); + sStandardActions.add(ACTION_CLEAR_SELECTION); + sStandardActions.add(ACTION_CLICK); + sStandardActions.add(ACTION_LONG_CLICK); + sStandardActions.add(ACTION_ACCESSIBILITY_FOCUS); + sStandardActions.add(ACTION_CLEAR_ACCESSIBILITY_FOCUS); + sStandardActions.add(ACTION_NEXT_AT_MOVEMENT_GRANULARITY); + sStandardActions.add(ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); + sStandardActions.add(ACTION_NEXT_HTML_ELEMENT); + sStandardActions.add(ACTION_PREVIOUS_HTML_ELEMENT); + sStandardActions.add(ACTION_SCROLL_FORWARD); + sStandardActions.add(ACTION_SCROLL_BACKWARD); + sStandardActions.add(ACTION_COPY); + sStandardActions.add(ACTION_PASTE); + sStandardActions.add(ACTION_CUT); + sStandardActions.add(ACTION_SET_SELECTION); + sStandardActions.add(ACTION_EXPAND); + sStandardActions.add(ACTION_COLLAPSE); + sStandardActions.add(ACTION_DISMISS); + sStandardActions.add(ACTION_SET_TEXT); + } + + private final int mActionId; + private final CharSequence mLabel; + + /** + * Creates a new AccessibilityAction. For adding a standard action without a specific label, + * use the static constants. + * + * You can also override the description for one the standard actions. Below is an example + * how to override the standard click action by adding a custom label: + * <pre> + * AccessibilityAction action = new AccessibilityAction( + * AccessibilityAction.ACTION_ACTION_CLICK, getLocalizedLabel()); + * node.addAction(action); + * </pre> + * + * @param actionId The id for this action. This should either be one of the + * standard actions or a specific action for your app. In that case it is + * required to use a resource identifier. + * @param label The label for the new AccessibilityAction. + */ + public AccessibilityAction(int actionId, @Nullable CharSequence label) { + if ((actionId & ACTION_TYPE_MASK) == 0 && Integer.bitCount(actionId) > 1) { + throw new IllegalArgumentException("Invalid standard action id"); + } + + if ((actionId & STANDARD_NON_LEGACY_ACTION_MASK) != 0) { + throw new IllegalArgumentException("action id not a resource id"); } + + mActionId = actionId; + mLabel = label; } - builder.append("]"); - return builder.toString(); + /** + * Gets the id for this action. + * + * @return The action id. + */ + public int getId() { + return mActionId; + } + + /** + * Gets the label for this action. Its purpose is to describe the + * action to user. + * + * @return The label. + */ + public CharSequence getLabel() { + return mLabel; + } + + @Override + public int hashCode() { + return mActionId; + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + + if (other == this) { + return true; + } + + if (getClass() != other.getClass()) { + return false; + } + + return mActionId == ((AccessibilityAction)other).mActionId; + } + + @Override + public String toString() { + return "AccessibilityAction: " + getActionSymbolicName(mActionId) + " - " + mLabel; + } } /** diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java index 92455df..fad6747 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfo.java +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -35,8 +35,12 @@ import java.util.Objects; public final class CursorAnchorInfo implements Parcelable { private final int mSelectionStart; private final int mSelectionEnd; - private final int mCandidatesStart; - private final int mCandidatesEnd; + + private final int mComposingTextStart; + /** + * The text, tracked as a composing region. + */ + private final String mComposingText; /** * Horizontal position of the insertion marker, in the local coordinates that will be @@ -83,8 +87,8 @@ public final class CursorAnchorInfo implements Parcelable { public CursorAnchorInfo(final Parcel source) { mSelectionStart = source.readInt(); mSelectionEnd = source.readInt(); - mCandidatesStart = source.readInt(); - mCandidatesEnd = source.readInt(); + mComposingTextStart = source.readInt(); + mComposingText = source.readString(); mInsertionMarkerHorizontal = source.readFloat(); mInsertionMarkerTop = source.readFloat(); mInsertionMarkerBaseline = source.readFloat(); @@ -104,8 +108,8 @@ public final class CursorAnchorInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mSelectionStart); dest.writeInt(mSelectionEnd); - dest.writeInt(mCandidatesStart); - dest.writeInt(mCandidatesEnd); + dest.writeInt(mComposingTextStart); + dest.writeString(mComposingText); dest.writeFloat(mInsertionMarkerHorizontal); dest.writeFloat(mInsertionMarkerTop); dest.writeFloat(mInsertionMarkerBaseline); @@ -119,14 +123,17 @@ public final class CursorAnchorInfo implements Parcelable { @Override public int hashCode(){ // TODO: Improve the hash function. - final float floatHash = mSelectionStart + mSelectionEnd + mCandidatesStart + mCandidatesEnd - + mInsertionMarkerHorizontal + mInsertionMarkerTop + mInsertionMarkerBaseline - + mInsertionMarkerBottom; + final float floatHash = mInsertionMarkerHorizontal + mInsertionMarkerTop + + mInsertionMarkerBaseline + mInsertionMarkerBottom; int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash); - if (mCharacterRects != null) { - hash += mCharacterRects.hashCode(); - } - hash += mMatrix.hashCode(); + hash *= 31; + hash += mSelectionStart + mSelectionEnd + mComposingTextStart; + hash *= 31; + hash += Objects.hashCode(mComposingText); + hash *= 31; + hash += Objects.hashCode(mCharacterRects); + hash *= 31; + hash += Objects.hashCode(mMatrix); return hash; } @@ -147,8 +154,10 @@ public final class CursorAnchorInfo implements Parcelable { } if (mSelectionStart != that.mSelectionStart || mSelectionEnd != that.mSelectionEnd - || mCandidatesStart != that.mCandidatesStart - || mCandidatesEnd != that.mCandidatesEnd) { + || mComposingTextStart != that.mComposingTextStart) { + return false; + } + if (!Objects.equals(mComposingTextStart, that.mComposingTextStart)) { return false; } if (!Objects.equals(mCharacterRects, that.mCharacterRects)) { @@ -163,13 +172,14 @@ public final class CursorAnchorInfo implements Parcelable { @Override public String toString() { return "SelectionInfo{mSelection=" + mSelectionStart + "," + mSelectionEnd - + " mCandiadtes=" + mCandidatesStart + "," + mCandidatesEnd + + " mComposingTextStart=" + mComposingTextStart + + " mComposingText=" + Objects.toString(mComposingText) + " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal + " mInsertionMarkerTop=" + mInsertionMarkerTop + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline + " mInsertionMarkerBottom=" + mInsertionMarkerBottom - + " mCharacterRects=" + (mCharacterRects != null ? mCharacterRects : "null") - + " mMatrix=" + mMatrix + + " mCharacterRects=" + Objects.toString(mCharacterRects) + + " mMatrix=" + Objects.toString(mMatrix) + "}"; } @@ -190,16 +200,23 @@ public final class CursorAnchorInfo implements Parcelable { private int mSelectionEnd = -1; /** - * Sets the text range of the composition string. Calling this can be skipped if there is - * no composition. + * Sets the text range of the composing text. Calling this can be skipped if there is + * no composing text. + * @param index index where the composing text starts. + * @param composingText the entire composing text. */ - public CursorAnchorInfoBuilder setCandidateRange(final int start, final int end) { - mCandidateStart = start; - mCandidateEnd = end; + public CursorAnchorInfoBuilder setComposingText(final int index, + final CharSequence composingText) { + mComposingTextStart = index; + if (composingText == null) { + mComposingText = null; + } else { + mComposingText = composingText.toString(); + } return this; } - private int mCandidateStart = -1; - private int mCandidateEnd = -1; + private int mComposingTextStart = -1; + private String mComposingText = null; /** * Sets the location of the text insertion point (zero width cursor) as a rectangle in @@ -273,14 +290,10 @@ public final class CursorAnchorInfo implements Parcelable { * is interpreted as an identity matrix. */ public CursorAnchorInfoBuilder setMatrix(final Matrix matrix) { - if (matrix != null) { - mMatrix = matrix; - } else { - mMatrix = Matrix.IDENTITY_MATRIX; - } + mMatrix.set(matrix != null ? matrix : Matrix.IDENTITY_MATRIX); return this; } - private Matrix mMatrix = Matrix.IDENTITY_MATRIX; + private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX); /** * @return {@link CursorAnchorInfo} using parameters in this @@ -297,13 +310,13 @@ public final class CursorAnchorInfo implements Parcelable { public void reset() { mSelectionStart = -1; mSelectionEnd = -1; - mCandidateStart = -1; - mCandidateEnd = -1; + mComposingTextStart = -1; + mComposingText = null; mInsertionMarkerHorizontal = Float.NaN; mInsertionMarkerTop = Float.NaN; mInsertionMarkerBaseline = Float.NaN; mInsertionMarkerBottom = Float.NaN; - mMatrix = Matrix.IDENTITY_MATRIX; + mMatrix.set(Matrix.IDENTITY_MATRIX); if (mCharacterRectBuilder != null) { mCharacterRectBuilder.reset(); } @@ -313,15 +326,15 @@ public final class CursorAnchorInfo implements Parcelable { private CursorAnchorInfo(final CursorAnchorInfoBuilder builder) { mSelectionStart = builder.mSelectionStart; mSelectionEnd = builder.mSelectionEnd; - mCandidatesStart = builder.mCandidateStart; - mCandidatesEnd = builder.mCandidateEnd; + mComposingTextStart = builder.mComposingTextStart; + mComposingText = builder.mComposingText; mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal; mInsertionMarkerTop = builder.mInsertionMarkerTop; mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline; mInsertionMarkerBottom = builder.mInsertionMarkerBottom; mCharacterRects = builder.mCharacterRectBuilder != null ? builder.mCharacterRectBuilder.build() : null; - mMatrix = builder.mMatrix; + mMatrix = new Matrix(builder.mMatrix); } /** @@ -341,19 +354,19 @@ public final class CursorAnchorInfo implements Parcelable { } /** - * Returns the index where the composition starts. - * @return -1 if there is no composition. + * Returns the index where the composing text starts. + * @return -1 if there is no composing text. */ - public int getCandidatesStart() { - return mCandidatesStart; + public int getComposingTextStart() { + return mComposingTextStart; } /** - * Returns the index where the composition ends. - * @return -1 if there is no composition. + * Returns the entire composing text. + * @return null if there is no composition. */ - public int getCandidatesEnd() { - return mCandidatesEnd; + public String getComposingText() { + return mComposingText; } /** diff --git a/core/java/android/webkit/BrowserDownloadListener.java b/core/java/android/webkit/BrowserDownloadListener.java deleted file mode 100644 index 724cc62..0000000 --- a/core/java/android/webkit/BrowserDownloadListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.webkit; - -/** - * An abstract download listener that allows passing extra information as - * part of onDownloadStart callback. - * @hide - */ -public abstract class BrowserDownloadListener implements DownloadListener { - - /** - * Notify the host application that a file should be downloaded - * @param url The full url to the content that should be downloaded - * @param userAgent the user agent to be used for the download. - * @param contentDisposition Content-disposition http header, if - * present. - * @param mimetype The mimetype of the content reported by the server - * @param referer The referer associated with this url - * @param contentLength The file size reported by the server - */ - public abstract void onDownloadStart(String url, String userAgent, - String contentDisposition, String mimetype, String referer, - long contentLength); - - - /** - * Notify the host application that a file should be downloaded - * @param url The full url to the content that should be downloaded - * @param userAgent the user agent to be used for the download. - * @param contentDisposition Content-disposition http header, if - * present. - * @param mimetype The mimetype of the content reported by the server - * @param contentLength The file size reported by the server - */ - @Override - public void onDownloadStart(String url, String userAgent, - String contentDisposition, String mimetype, long contentLength) { - - onDownloadStart(url, userAgent, contentDisposition, mimetype, null, - contentLength); - } -} diff --git a/core/java/android/webkit/WebBackForwardListClient.java b/core/java/android/webkit/WebBackForwardListClient.java deleted file mode 100644 index 7fe9281..0000000 --- a/core/java/android/webkit/WebBackForwardListClient.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.webkit; - -/** - * Interface to receive notifications when items are added to the - * {@link WebBackForwardList}. - * {@hide} - */ -public abstract class WebBackForwardListClient { - - /** - * Notify the client that <var>item</var> has been added to the - * WebBackForwardList. - * @param item The newly created WebHistoryItem - */ - public void onNewHistoryItem(WebHistoryItem item) { } - - /** - * Notify the client that the <var>item</var> at <var>index</var> is now - * the current history item. - * @param item A WebHistoryItem - * @param index The new history index - */ - public void onIndexChanged(WebHistoryItem item, int index) { } -} diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index cbe7511..27d6b82 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3041,8 +3041,7 @@ public class Editor { builder.reset(); final int selectionStart = mTextView.getSelectionStart(); - final int selectionEnd = mTextView.getSelectionEnd(); - builder.setSelectionRange(mTextView.getSelectionStart(), mTextView.getSelectionEnd()); + builder.setSelectionRange(selectionStart, mTextView.getSelectionEnd()); // Construct transformation matrix from view local coordinates to screen coordinates. mViewToScreenMatrix.set(mTextView.getMatrix()); @@ -3055,17 +3054,24 @@ public class Editor { final float viewportToContentVerticalOffset = mTextView.viewportToContentVerticalOffset(); - if (mTextView.getText() instanceof Spannable) { - final Spannable sp = (Spannable) mTextView.getText(); - int compositionStart = EditableInputConnection.getComposingSpanStart(sp); - int compositionEnd = EditableInputConnection.getComposingSpanEnd(sp); - if (compositionEnd < compositionStart) { - final int temp = compositionEnd; - compositionEnd = compositionStart; - compositionStart = temp; + final CharSequence text = mTextView.getText(); + if (text instanceof Spannable) { + final Spannable sp = (Spannable) text; + int composingTextStart = EditableInputConnection.getComposingSpanStart(sp); + int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp); + if (composingTextEnd < composingTextStart) { + final int temp = composingTextEnd; + composingTextEnd = composingTextStart; + composingTextStart = temp; + } + final boolean hasComposingText = + (0 <= composingTextStart) && (composingTextStart < composingTextEnd); + if (hasComposingText) { + final CharSequence composingText = text.subSequence(composingTextStart, + composingTextEnd); + builder.setComposingText(composingTextStart, composingText); } - builder.setCandidateRange(compositionStart, compositionEnd); - for (int offset = compositionStart; offset < compositionEnd; offset++) { + for (int offset = composingTextStart; offset < composingTextEnd; offset++) { if (offset < 0) { continue; } diff --git a/core/java/android/widget/RtlSpacingHelper.java b/core/java/android/widget/RtlSpacingHelper.java new file mode 100644 index 0000000..f6b116f --- /dev/null +++ b/core/java/android/widget/RtlSpacingHelper.java @@ -0,0 +1,91 @@ +/* + * 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.widget; + +/** + * RtlSpacingHelper manages the relationship between left/right and start/end for views + * that need to maintain both absolute and relative settings for a form of spacing similar + * to view padding. + */ +class RtlSpacingHelper { + public static final int UNDEFINED = Integer.MIN_VALUE; + + private int mLeft = 0; + private int mRight = 0; + private int mStart = UNDEFINED; + private int mEnd = UNDEFINED; + private int mExplicitLeft = 0; + private int mExplicitRight = 0; + + private boolean mIsRtl = false; + private boolean mIsRelative = false; + + public int getLeft() { + return mLeft; + } + + public int getRight() { + return mRight; + } + + public int getStart() { + return mIsRtl ? mRight : mLeft; + } + + public int getEnd() { + return mIsRtl ? mLeft : mRight; + } + + public void setRelative(int start, int end) { + mStart = start; + mEnd = end; + mIsRelative = true; + if (mIsRtl) { + if (end != UNDEFINED) mLeft = end; + if (start != UNDEFINED) mRight = start; + } else { + if (start != UNDEFINED) mLeft = start; + if (end != UNDEFINED) mRight = end; + } + } + + public void setAbsolute(int left, int right) { + mIsRelative = false; + if (left != UNDEFINED) mLeft = mExplicitLeft = left; + if (right != UNDEFINED) mRight = mExplicitRight = right; + } + + public void setDirection(boolean isRtl) { + if (isRtl == mIsRtl) { + return; + } + mIsRtl = isRtl; + if (mIsRelative) { + if (isRtl) { + mLeft = mEnd != UNDEFINED ? mEnd : mExplicitLeft; + mRight = mStart != UNDEFINED ? mStart : mExplicitRight; + } else { + mLeft = mStart != UNDEFINED ? mStart : mExplicitLeft; + mRight = mEnd != UNDEFINED ? mEnd : mExplicitRight; + } + } else { + mLeft = mExplicitLeft; + mRight = mExplicitRight; + } + } +} diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index 075feba..f903346 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -88,11 +88,14 @@ public class Toolbar extends ViewGroup { private int mTitleTextAppearance; private int mSubtitleTextAppearance; + private int mTitleMarginStart; private int mTitleMarginEnd; private int mTitleMarginTop; private int mTitleMarginBottom; + private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper(); + private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL; private CharSequence mTitleText; @@ -135,8 +138,8 @@ public class Toolbar extends ViewGroup { mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0); mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0); mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity); - mTitleMarginStart = mTitleMarginEnd = Math.max(0, a.getDimensionPixelOffset( - R.styleable.Toolbar_titleMargins, -1)); + mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom = + a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0); final int marginStart = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMarginStart, -1); if (marginStart >= 0) { @@ -159,6 +162,24 @@ public class Toolbar extends ViewGroup { mTitleMarginBottom = marginBottom; } + final int contentInsetStart = + a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetStart, + RtlSpacingHelper.UNDEFINED); + final int contentInsetEnd = + a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetEnd, + RtlSpacingHelper.UNDEFINED); + final int contentInsetLeft = + a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetLeft, 0); + final int contentInsetRight = + a.getDimensionPixelSize(R.styleable.Toolbar_contentInsetRight, 0); + + mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight); + + if (contentInsetStart != RtlSpacingHelper.UNDEFINED || + contentInsetEnd != RtlSpacingHelper.UNDEFINED) { + mContentInsets.setRelative(contentInsetStart, contentInsetEnd); + } + final CharSequence title = a.getText(R.styleable.Toolbar_title); if (!TextUtils.isEmpty(title)) { setTitle(title); @@ -171,6 +192,12 @@ public class Toolbar extends ViewGroup { a.recycle(); } + @Override + public void onRtlPropertiesChanged(@ResolvedLayoutDir int layoutDirection) { + super.onRtlPropertiesChanged(layoutDirection); + mContentInsets.setDirection(layoutDirection == LAYOUT_DIRECTION_RTL); + } + /** * Set a logo drawable from a resource id. * @@ -489,6 +516,122 @@ public class Toolbar extends ViewGroup { mOnMenuItemClickListener = listener; } + /** + * Set the content insets for this toolbar relative to layout direction. + * + * <p>The content inset affects the valid area for Toolbar content other than + * the navigation button and menu. Insets define the minimum margin for these components + * and can be used to effectively align Toolbar content along well-known gridlines.</p> + * + * @param contentInsetStart Content inset for the toolbar starting edge + * @param contentInsetEnd Content inset for the toolbar ending edge + * + * @see #setContentInsetsAbsolute(int, int) + * @see #getContentInsetStart() + * @see #getContentInsetEnd() + * @see #getContentInsetLeft() + * @see #getContentInsetRight() + */ + public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) { + mContentInsets.setRelative(contentInsetStart, contentInsetEnd); + } + + /** + * Get the starting content inset for this toolbar. + * + * <p>The content inset affects the valid area for Toolbar content other than + * the navigation button and menu. Insets define the minimum margin for these components + * and can be used to effectively align Toolbar content along well-known gridlines.</p> + * + * @return The starting content inset for this toolbar + * + * @see #setContentInsetsRelative(int, int) + * @see #setContentInsetsAbsolute(int, int) + * @see #getContentInsetEnd() + * @see #getContentInsetLeft() + * @see #getContentInsetRight() + */ + public int getContentInsetStart() { + return mContentInsets.getStart(); + } + + /** + * Get the ending content inset for this toolbar. + * + * <p>The content inset affects the valid area for Toolbar content other than + * the navigation button and menu. Insets define the minimum margin for these components + * and can be used to effectively align Toolbar content along well-known gridlines.</p> + * + * @return The ending content inset for this toolbar + * + * @see #setContentInsetsRelative(int, int) + * @see #setContentInsetsAbsolute(int, int) + * @see #getContentInsetStart() + * @see #getContentInsetLeft() + * @see #getContentInsetRight() + */ + public int getContentInsetEnd() { + return mContentInsets.getEnd(); + } + + /** + * Set the content insets for this toolbar. + * + * <p>The content inset affects the valid area for Toolbar content other than + * the navigation button and menu. Insets define the minimum margin for these components + * and can be used to effectively align Toolbar content along well-known gridlines.</p> + * + * @param contentInsetLeft Content inset for the toolbar's left edge + * @param contentInsetRight Content inset for the toolbar's right edge + * + * @see #setContentInsetsAbsolute(int, int) + * @see #getContentInsetStart() + * @see #getContentInsetEnd() + * @see #getContentInsetLeft() + * @see #getContentInsetRight() + */ + public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) { + mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight); + } + + /** + * Get the left content inset for this toolbar. + * + * <p>The content inset affects the valid area for Toolbar content other than + * the navigation button and menu. Insets define the minimum margin for these components + * and can be used to effectively align Toolbar content along well-known gridlines.</p> + * + * @return The left content inset for this toolbar + * + * @see #setContentInsetsRelative(int, int) + * @see #setContentInsetsAbsolute(int, int) + * @see #getContentInsetStart() + * @see #getContentInsetEnd() + * @see #getContentInsetRight() + */ + public int getContentInsetLeft() { + return mContentInsets.getLeft(); + } + + /** + * Get the right content inset for this toolbar. + * + * <p>The content inset affects the valid area for Toolbar content other than + * the navigation button and menu. Insets define the minimum margin for these components + * and can be used to effectively align Toolbar content along well-known gridlines.</p> + * + * @return The right content inset for this toolbar + * + * @see #setContentInsetsRelative(int, int) + * @see #setContentInsetsAbsolute(int, int) + * @see #getContentInsetStart() + * @see #getContentInsetEnd() + * @see #getContentInsetLeft() + */ + public int getContentInsetRight() { + return mContentInsets.getRight(); + } + private void ensureNavButtonView() { if (mNavButtonView == null) { mNavButtonView = new ImageButton(getContext(), null, R.attr.borderlessButtonStyle); @@ -522,22 +665,28 @@ public class Toolbar extends ViewGroup { // System views measure first. + int navWidth = 0; if (shouldLayout(mNavButtonView)) { measureChildWithMargins(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0); - width += mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView); + navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView); height = Math.max(height, mNavButtonView.getMeasuredHeight() + getVerticalMargins(mNavButtonView)); childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState()); } + width += Math.max(getContentInsetStart(), navWidth); + + int menuWidth = 0; if (shouldLayout(mMenuView)) { measureChildWithMargins(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0); - width += mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView); + menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView); height = Math.max(height, mMenuView.getMeasuredHeight() + getVerticalMargins(mMenuView)); childState = combineMeasuredStates(childState, mMenuView.getMeasuredState()); } + width += Math.max(getContentInsetEnd(), menuWidth); + if (shouldLayout(mLogoView)) { measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0); width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView); @@ -627,6 +776,9 @@ public class Toolbar extends ViewGroup { } } + left = Math.max(left, getContentInsetLeft()); + right = Math.min(right, width - paddingRight - getContentInsetRight()); + if (shouldLayout(mLogoView)) { if (isRtl) { right = layoutChildRight(mLogoView, right); @@ -734,13 +886,13 @@ public class Toolbar extends ViewGroup { addCustomViewsWithGravity(mTempViews, Gravity.LEFT); final int leftViewsCount = mTempViews.size(); for (int i = 0; i < leftViewsCount; i++) { - left = layoutChildLeft(getChildAt(i), left); + left = layoutChildLeft(mTempViews.get(i), left); } addCustomViewsWithGravity(mTempViews, Gravity.RIGHT); final int rightViewsCount = mTempViews.size(); for (int i = 0; i < rightViewsCount; i++) { - right = layoutChildRight(getChildAt(i), right); + right = layoutChildRight(mTempViews.get(i), right); } // Centered views try to center with respect to the whole bar, but views pinned @@ -759,7 +911,7 @@ public class Toolbar extends ViewGroup { final int centerViewsCount = mTempViews.size(); for (int i = 0; i < centerViewsCount; i++) { - centerLeft = layoutChildLeft(getChildAt(i), centerLeft); + centerLeft = layoutChildLeft(mTempViews.get(i), centerLeft); } mTempViews.clear(); } @@ -778,18 +930,20 @@ public class Toolbar extends ViewGroup { private int layoutChildLeft(View child, int left) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); left += lp.leftMargin; - int top = getChildTop(child); - child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); - left += lp.rightMargin; + final int top = getChildTop(child); + final int childWidth = child.getMeasuredWidth(); + child.layout(left, top, left + childWidth, top + child.getMeasuredHeight()); + left += childWidth + lp.rightMargin; return left; } private int layoutChildRight(View child, int right) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); right -= lp.rightMargin; - int top = getChildTop(child); - child.layout(right - child.getMeasuredWidth(), top, right, top + child.getMeasuredHeight()); - right -= lp.leftMargin; + final int top = getChildTop(child); + final int childWidth = child.getMeasuredWidth(); + child.layout(right - childWidth, top, right, top + child.getMeasuredHeight()); + right -= childWidth + lp.leftMargin; return right; } diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index f23c64f..2b62552 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -30,6 +30,7 @@ import android.media.MediaPlayer.OnInfoListener; import android.media.Metadata; import android.media.SubtitleController; import android.media.SubtitleTrack.RenderingWidget; +import android.media.TtmlRenderer; import android.media.WebVttRenderer; import android.net.Uri; import android.os.Looper; @@ -314,6 +315,7 @@ public class VideoView extends SurfaceView final SubtitleController controller = new SubtitleController( context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer); controller.registerRenderer(new WebVttRenderer(context)); + controller.registerRenderer(new TtmlRenderer(context)); mMediaPlayer.setSubtitleAnchor(controller, this); if (mAudioSession != 0) { diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index cd75010..8e6fa58 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -17,6 +17,7 @@ package com.android.internal.app; import android.app.AppOpsManager; +import android.os.Bundle; import com.android.internal.app.IAppOpsCallback; interface IAppOpsService { @@ -38,4 +39,10 @@ interface IAppOpsService { void resetAllModes(); int checkAudioOperation(int code, int stream, int uid, String packageName); void setAudioRestriction(int code, int stream, int uid, int mode, in String[] exceptionPackages); + + void setDeviceOwner(String packageName); + void setProfileOwner(String packageName, int userHandle); + void setUserRestrictions(in Bundle restrictions, int userHandle); + void removeUser(int userHandle); + } diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 04547495..0769b08 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -46,14 +46,15 @@ interface IBatteryStats { void noteStartWakelock(int uid, int pid, String name, String historyName, int type, boolean unimportantForLogging); - void noteStopWakelock(int uid, int pid, String name, int type); + void noteStopWakelock(int uid, int pid, String name, String historyName, int type); void noteStartWakelockFromSource(in WorkSource ws, int pid, String name, String historyName, int type, boolean unimportantForLogging); - void noteChangeWakelockFromSource(in WorkSource ws, int pid, String name, int type, - in WorkSource newWs, int newPid, String newName, + void noteChangeWakelockFromSource(in WorkSource ws, int pid, String name, String histyoryName, + int type, in WorkSource newWs, int newPid, String newName, String newHistoryName, int newType, boolean newUnimportantForLogging); - void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, int type); + void noteStopWakelockFromSource(in WorkSource ws, int pid, String name, String historyName, + int type); void noteVibratorOn(int uid, long durationMillis); void noteVibratorOff(int uid); diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java index 66548f0..79a8f44 100644 --- a/core/java/com/android/internal/app/WindowDecorActionBar.java +++ b/core/java/com/android/internal/app/WindowDecorActionBar.java @@ -268,7 +268,7 @@ public class WindowDecorActionBar extends ActionBar implements if (getNavigationMode() == NAVIGATION_MODE_TABS) { tabScroller.setVisibility(View.VISIBLE); if (mOverlayLayout != null) { - mOverlayLayout.requestFitSystemWindows(); + mOverlayLayout.requestApplyInsets(); } } else { tabScroller.setVisibility(View.GONE); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7bd5b12..956c86d 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -48,7 +48,6 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; import android.util.SparseArray; -import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.TimeUtils; import android.view.Display; @@ -89,7 +88,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 105 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 106 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -236,6 +235,7 @@ public final class BatteryStatsImpl extends BatteryStats { int mWakeLockNesting; boolean mWakeLockImportant; + public boolean mRecordAllWakeLocks; int mScreenState = Display.STATE_UNKNOWN; StopwatchTimer mScreenOnTimer; @@ -246,6 +246,9 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mInteractive; StopwatchTimer mInteractiveTimer; + boolean mLowPowerModeEnabled; + StopwatchTimer mLowPowerModeEnabledTimer; + boolean mPhoneOn; StopwatchTimer mPhoneOnTimer; @@ -2314,6 +2317,14 @@ public final class BatteryStatsImpl extends BatteryStats { private String mInitialAcquireWakeName; private int mInitialAcquireWakeUid = -1; + public void setRecordAllWakeLocksLocked(boolean enabled) { + mRecordAllWakeLocks = enabled; + if (!enabled) { + // Clear out any existing state. + mActiveEvents.removeEvents(HistoryItem.EVENT_WAKE_LOCK); + } + } + public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type, boolean unimportantForLogging, long elapsedRealtime, long uptime) { uid = mapUid(uid); @@ -2321,27 +2332,33 @@ public final class BatteryStatsImpl extends BatteryStats { // Only care about partial wake locks, since full wake locks // will be canceled when the user puts the screen to sleep. aggregateLastWakeupUptimeLocked(uptime); + historyName = historyName == null || mRecordAllWakeLocks ? name : historyName; if (mWakeLockNesting == 0) { mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: " + Integer.toHexString(mHistoryCur.states)); mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = mInitialAcquireWakeName - = historyName != null ? historyName : name; + mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName; mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid; mWakeLockImportant = !unimportantForLogging; addHistoryRecordLocked(elapsedRealtime, uptime); - } else if (!mWakeLockImportant && !unimportantForLogging) { + } else if (!mRecordAllWakeLocks && !mWakeLockImportant && !unimportantForLogging) { if (mHistoryLastWritten.wakelockTag != null) { // We'll try to update the last tag. mHistoryLastWritten.wakelockTag = null; mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = mInitialAcquireWakeName - = historyName != null ? historyName : name; + mHistoryCur.wakelockTag.string = mInitialAcquireWakeName = historyName; mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid; addHistoryRecordLocked(elapsedRealtime, uptime); } mWakeLockImportant = true; + } else if (mRecordAllWakeLocks) { + if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName, + uid, 0)) { + return; + } + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_WAKE_LOCK_START, + historyName, uid); } mWakeLockNesting++; } @@ -2354,24 +2371,33 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime, - long uptime) { + public void noteStopWakeLocked(int uid, int pid, String name, String historyName, int type, + long elapsedRealtime, long uptime) { uid = mapUid(uid); if (type == WAKE_TYPE_PARTIAL) { mWakeLockNesting--; + historyName = historyName == null || mRecordAllWakeLocks ? name : historyName; if (mWakeLockNesting == 0) { mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: " + Integer.toHexString(mHistoryCur.states)); - if ((name != null && !name.equals(mInitialAcquireWakeName)) + if (mRecordAllWakeLocks + || (historyName != null && !historyName.equals(mInitialAcquireWakeName)) || uid != mInitialAcquireWakeUid) { mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = name; + mHistoryCur.wakelockTag.string = historyName; mHistoryCur.wakelockTag.uid = uid; } mInitialAcquireWakeName = null; mInitialAcquireWakeUid = -1; addHistoryRecordLocked(elapsedRealtime, uptime); + } else if (mRecordAllWakeLocks) { + if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, + uid, 0)) { + return; + } + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_WAKE_LOCK_FINISH, + historyName, uid); } } if (uid >= 0) { @@ -2391,8 +2417,8 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name, int type, - WorkSource newWs, int newPid, String newName, + public void noteChangeWakelockFromSourceLocked(WorkSource ws, int pid, String name, + String historyName, int type, WorkSource newWs, int newPid, String newName, String newHistoryName, int newType, boolean newUnimportantForLogging) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); @@ -2406,16 +2432,17 @@ public final class BatteryStatsImpl extends BatteryStats { } final int NO = ws.size(); for (int i=0; i<NO; i++) { - noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime, uptime); + noteStopWakeLocked(ws.get(i), pid, name, historyName, type, elapsedRealtime, uptime); } } - public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) { + public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, + String historyName, int type) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); final int N = ws.size(); for (int i=0; i<N; i++) { - noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime, uptime); + noteStopWakeLocked(ws.get(i), pid, name, historyName, type, elapsedRealtime, uptime); } } @@ -2684,7 +2711,7 @@ public final class BatteryStatsImpl extends BatteryStats { mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime); } - noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL, + noteStopWakeLocked(-1, -1, "screen", "screen", WAKE_TYPE_PARTIAL, elapsedRealtime, uptime); updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true, @@ -2780,6 +2807,26 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public void noteLowPowerMode(boolean enabled) { + if (mLowPowerModeEnabled != enabled) { + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long uptime = SystemClock.uptimeMillis(); + mLowPowerModeEnabled = enabled; + if (enabled) { + mHistoryCur.states2 |= HistoryItem.STATE2_LOW_POWER_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "Low power mode enabled to: " + + Integer.toHexString(mHistoryCur.states2)); + mLowPowerModeEnabledTimer.startRunningLocked(elapsedRealtime); + } else { + mHistoryCur.states2 &= ~HistoryItem.STATE2_LOW_POWER_FLAG; + if (DEBUG_HISTORY) Slog.v(TAG, "Low power mode disabled to: " + + Integer.toHexString(mHistoryCur.states2)); + mLowPowerModeEnabledTimer.stopRunningLocked(elapsedRealtime); + } + addHistoryRecordLocked(elapsedRealtime, uptime); + } + } + public void notePhoneOnLocked() { if (!mPhoneOn) { final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -3443,6 +3490,14 @@ public final class BatteryStatsImpl extends BatteryStats { return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } + @Override public long getLowPowerModeEnabledTime(long elapsedRealtimeUs, int which) { + return mLowPowerModeEnabledTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + } + + @Override public int getLowPowerModeEnabledCount(int which) { + return mLowPowerModeEnabledTimer.getCountLocked(which); + } + @Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) { return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -5489,7 +5544,8 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mOnBatteryTimeBase); } - mPhoneOnTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase); + mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase); + mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null, mOnBatteryTimeBase); @@ -5508,18 +5564,18 @@ public final class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase); mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase); mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase); - mWifiOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase); - mGlobalWifiRunningTimer = new StopwatchTimer(null, -4, null, mOnBatteryTimeBase); + mWifiOnTimer = new StopwatchTimer(null, -4, null, mOnBatteryTimeBase); + mGlobalWifiRunningTimer = new StopwatchTimer(null, -5, null, mOnBatteryTimeBase); for (int i=0; i<NUM_WIFI_STATES; i++) { mWifiStateTimer[i] = new StopwatchTimer(null, -600-i, null, mOnBatteryTimeBase); } - mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mOnBatteryTimeBase); + mBluetoothOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase); for (int i=0; i< NUM_BLUETOOTH_STATES; i++) { mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i, null, mOnBatteryTimeBase); } - mAudioOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase); - mVideoOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase); - mInteractiveTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase); + mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase); + mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase); + mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase); mOnBattery = mOnBatteryInternal = false; long uptime = SystemClock.uptimeMillis() * 1000; long realtime = SystemClock.elapsedRealtime() * 1000; @@ -5765,6 +5821,7 @@ public final class BatteryStatsImpl extends BatteryStats { mScreenBrightnessTimer[i].reset(false); } mInteractiveTimer.reset(false); + mLowPowerModeEnabledTimer.reset(false); mPhoneOnTimer.reset(false); mAudioOnTimer.reset(false); mVideoOnTimer.reset(false); @@ -6915,6 +6972,7 @@ public final class BatteryStatsImpl extends BatteryStats { mInteractive = false; mInteractiveTimer.readSummaryFromParcelLocked(in); mPhoneOn = false; + mLowPowerModeEnabledTimer.readSummaryFromParcelLocked(in); mPhoneOnTimer.readSummaryFromParcelLocked(in); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in); @@ -7169,6 +7227,7 @@ public final class BatteryStatsImpl extends BatteryStats { mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); } mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); + mLowPowerModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); @@ -7431,7 +7490,8 @@ public final class BatteryStatsImpl extends BatteryStats { in); } mPhoneOn = false; - mPhoneOnTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in); + mLowPowerModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in); + mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null, mOnBatteryTimeBase, in); @@ -7453,25 +7513,25 @@ public final class BatteryStatsImpl extends BatteryStats { mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in); mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in); mWifiOn = false; - mWifiOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in); + mWifiOnTimer = new StopwatchTimer(null, -4, null, mOnBatteryTimeBase, in); mGlobalWifiRunning = false; - mGlobalWifiRunningTimer = new StopwatchTimer(null, -4, null, mOnBatteryTimeBase, in); + mGlobalWifiRunningTimer = new StopwatchTimer(null, -5, null, mOnBatteryTimeBase, in); for (int i=0; i<NUM_WIFI_STATES; i++) { mWifiStateTimer[i] = new StopwatchTimer(null, -600-i, null, mOnBatteryTimeBase, in); } mBluetoothOn = false; - mBluetoothOnTimer = new StopwatchTimer(null, -5, null, mOnBatteryTimeBase, in); + mBluetoothOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase, in); for (int i=0; i< NUM_BLUETOOTH_STATES; i++) { mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i, null, mOnBatteryTimeBase, in); } mAudioOn = false; - mAudioOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase); + mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase); mVideoOn = false; - mVideoOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase); + mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase); mInteractive = false; - mInteractiveTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase, in); + mInteractiveTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in); mDischargeUnplugLevel = in.readInt(); mDischargePlugLevel = in.readInt(); mDischargeCurrentLevel = in.readInt(); @@ -7570,6 +7630,7 @@ public final class BatteryStatsImpl extends BatteryStats { mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime); } mInteractiveTimer.writeToParcel(out, uSecRealtime); + mLowPowerModeEnabledTimer.writeToParcel(out, uSecRealtime); mPhoneOnTimer.writeToParcel(out, uSecRealtime); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime); @@ -7688,6 +7749,8 @@ public final class BatteryStatsImpl extends BatteryStats { } pr.println("*** Interactive timer:"); mInteractiveTimer.logState(pr, " "); + pr.println("*** Low power mode timer:"); + mLowPowerModeEnabledTimer.logState(pr, " "); pr.println("*** Phone timer:"); mPhoneOnTimer.logState(pr, " "); for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) { diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 3ea749e..5ce658b 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -23,6 +23,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.net.LocalServerSocket; import android.opengl.EGL14; +import android.os.Build; import android.os.Debug; import android.os.Process; import android.os.SystemClock; @@ -505,7 +506,7 @@ public class ZygoteInit { /** * Prepare the arguments and fork for the system server process. */ - private static boolean startSystemServer() + private static boolean startSystemServer(String abiList, String socketName) throws MethodAndArgsCaller, RuntimeException { long capabilities = posixCapabilitiesAsBits( OsConstants.CAP_BLOCK_SUSPEND, @@ -553,6 +554,10 @@ public class ZygoteInit { /* For child process */ if (pid == 0) { + if (hasSecondZygote(abiList)) { + waitForSecondaryZygote(socketName); + } + handleSystemServerProcess(parsedArgs); } @@ -615,7 +620,7 @@ public class ZygoteInit { Trace.setTracingEnabled(false); if (startSystemServer) { - startSystemServer(); + startSystemServer(abiList, socketName); } Log.i(TAG, "Accepting command socket connections"); @@ -632,6 +637,36 @@ public class ZygoteInit { } /** + * Return {@code true} if this device configuration has another zygote. + * + * We determine this by comparing the device ABI list with this zygotes + * list. If this zygote supports all ABIs this device supports, there won't + * be another zygote. + */ + private static boolean hasSecondZygote(String abiList) { + return !SystemProperties.get("ro.product.cpu.abilist").equals(abiList); + } + + private static void waitForSecondaryZygote(String socketName) { + String otherZygoteName = Process.ZYGOTE_SOCKET.equals(socketName) ? + Process.SECONDARY_ZYGOTE_SOCKET : Process.ZYGOTE_SOCKET; + while (true) { + try { + final Process.ZygoteState zs = Process.ZygoteState.connect(otherZygoteName); + zs.close(); + break; + } catch (IOException ioe) { + Log.w(TAG, "Got error connecting to zygote, retrying. msg= " + ioe.getMessage()); + } + + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + } + } + } + + /** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index d177410..a56fa36 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -117,6 +117,13 @@ public class ArrayUtils } /** + * Checks if given array is null or has zero elements. + */ + public static <T> boolean isEmpty(T[] array) { + return array == null || array.length == 0; + } + + /** * Checks that value is present as at least one of the elements of the array. * @param array the array to check in * @param value the value to check for diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java index 772dc5f..841a02a 100644 --- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java +++ b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java @@ -238,6 +238,10 @@ public class GlowPadView extends View { Drawable pointDrawable = pointId != 0 ? context.getDrawable(pointId) : null; mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f); + mPointCloud = new PointCloud(pointDrawable); + mPointCloud.makePointCloud(mInnerRadius, mOuterRadius); + mPointCloud.glowManager.setRadius(mGlowRadius); + TypedValue outValue = new TypedValue(); // Read array of target drawables @@ -273,10 +277,6 @@ public class GlowPadView extends View { setVibrateEnabled(mVibrationDuration > 0); assignDefaultsIfNeeded(); - - mPointCloud = new PointCloud(pointDrawable); - mPointCloud.makePointCloud(mInnerRadius, mOuterRadius); - mPointCloud.glowManager.setRadius(mGlowRadius); } private int getResourceId(TypedArray a, int id) { diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 7dc639d..0ad2ab2 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -5,6 +5,7 @@ LOCAL_CFLAGS += -DHAVE_CONFIG_H -DKHTML_NO_EXCEPTIONS -DGKWQ_NO_JAVA LOCAL_CFLAGS += -DNO_SUPPORT_JS_BINDING -DQT_NO_WHEELEVENT -DKHTML_NO_XBL LOCAL_CFLAGS += -U__APPLE__ LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast +LOCAL_CFLAGS += -Wno-non-virtual-dtor LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses LOCAL_CPPFLAGS += -Wno-conversion-null @@ -95,11 +96,11 @@ LOCAL_SRC_FILES:= \ android/graphics/CanvasProperty.cpp \ android/graphics/ColorFilter.cpp \ android/graphics/DrawFilter.cpp \ + android/graphics/FontFamily.cpp \ android/graphics/CreateJavaOutputStreamAdaptor.cpp \ android/graphics/Graphics.cpp \ android/graphics/HarfBuzzNGFaceSkia.cpp \ android/graphics/Interpolator.cpp \ - android/graphics/LayerRasterizer.cpp \ android/graphics/MaskFilter.cpp \ android/graphics/Matrix.cpp \ android/graphics/Movie.cpp \ @@ -125,6 +126,7 @@ LOCAL_SRC_FILES:= \ android/graphics/Xfermode.cpp \ android/graphics/YuvToJpegEncoder.cpp \ android/graphics/pdf/PdfDocument.cpp \ + android/graphics/pdf/PdfRenderer.cpp \ android_media_AudioRecord.cpp \ android_media_AudioSystem.cpp \ android_media_AudioTrack.cpp \ @@ -169,6 +171,9 @@ LOCAL_C_INCLUDES += \ $(call include-path-for, libhardware_legacy)/hardware_legacy \ $(TOP)/frameworks/av/include \ $(TOP)/system/media/camera/include \ + external/pdfrenderer/core/include/fpdfapi \ + external/pdfrenderer/core/include/fpdfdoc \ + external/pdfrenderer/fpdfsdk/include \ external/skia/src/core \ external/skia/src/effects \ external/skia/src/images \ @@ -222,6 +227,7 @@ LOCAL_SHARED_LIBRARIES := \ libharfbuzz_ng \ libz \ libaudioutils \ + libpdfrenderer \ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SHARED_LIBRARIES += libhwui @@ -231,7 +237,8 @@ ifeq ($(USE_MINIKIN), true) LOCAL_CFLAGS += -DUSE_MINIKIN LOCAL_C_INCLUDES += frameworks/minikin/include \ external/freetype/include - LOCAL_SRC_FILES += android/graphics/MinikinSkia.cpp + LOCAL_SRC_FILES += android/graphics/MinikinSkia.cpp \ + android/graphics/MinikinUtils.cpp # note: the freetype include is spurious; minikin itself probably # shouldn't depend on it LOCAL_SHARED_LIBRARIES += libminikin libstlport diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 01d8814..a4dc824 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -56,7 +56,6 @@ extern int register_android_graphics_Camera(JNIEnv* env); extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_Interpolator(JNIEnv* env); -extern int register_android_graphics_LayerRasterizer(JNIEnv*); extern int register_android_graphics_MaskFilter(JNIEnv* env); extern int register_android_graphics_Movie(JNIEnv* env); extern int register_android_graphics_NinePatch(JNIEnv*); @@ -108,6 +107,8 @@ extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); extern int register_android_graphics_DrawFilter(JNIEnv* env); +extern int register_android_graphics_FontFamily(JNIEnv* env); +extern int register_android_graphics_LayerRasterizer(JNIEnv*); extern int register_android_graphics_Matrix(JNIEnv* env); extern int register_android_graphics_Paint(JNIEnv* env); extern int register_android_graphics_Path(JNIEnv* env); @@ -119,6 +120,7 @@ extern int register_android_graphics_Region(JNIEnv* env); extern int register_android_graphics_SurfaceTexture(JNIEnv* env); extern int register_android_graphics_Xfermode(JNIEnv* env); extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env); +extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env); extern int register_android_view_DisplayEventReceiver(JNIEnv* env); extern int register_android_view_RenderNode(JNIEnv* env); extern int register_android_view_RenderNodeAnimator(JNIEnv* env); @@ -472,6 +474,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX]; char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX]; char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX]; + char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX]; char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX]; char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX]; char dalvikVmLibBuf[PROPERTY_VALUE_MAX]; @@ -625,6 +628,13 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) mOptions.add(opt); } + strcpy(backgroundgcOptsBuf, "-XX:BackgroundGC="); + property_get("dalvik.vm.backgroundgctype", backgroundgcOptsBuf+sizeof("-XX:BackgroundGC=")-1, ""); + if (backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1] != '\0') { + opt.optionString = backgroundgcOptsBuf; + mOptions.add(opt); + } + /* * Enable or disable dexopt features, such as bytecode verification and * calculation of register maps for precise GC. @@ -1228,6 +1238,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_CanvasProperty), REG_JNI(register_android_graphics_ColorFilter), REG_JNI(register_android_graphics_DrawFilter), + REG_JNI(register_android_graphics_FontFamily), REG_JNI(register_android_graphics_Interpolator), REG_JNI(register_android_graphics_LayerRasterizer), REG_JNI(register_android_graphics_MaskFilter), @@ -1248,6 +1259,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_graphics_Xfermode), REG_JNI(register_android_graphics_YuvImage), REG_JNI(register_android_graphics_pdf_PdfDocument), + REG_JNI(register_android_graphics_pdf_PdfRenderer), REG_JNI(register_android_database_CursorWindow), REG_JNI(register_android_database_SQLiteConnection), diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 2adbf3a..f7acbd7 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -30,6 +30,7 @@ #ifdef USE_MINIKIN #include <minikin/Layout.h> #include "MinikinSkia.h" +#include "MinikinUtils.h" #endif #include "TextLayout.h" @@ -820,7 +821,7 @@ public: } #ifdef USE_MINIKIN - static void drawGlyphsToSkia(SkCanvas *canvas, SkPaint *paint, Layout *layout, float x, float y) { + static void drawGlyphsToSkia(SkCanvas* canvas, SkPaint* paint, Layout* layout, float x, float y) { size_t nGlyphs = layout->nGlyphs(); uint16_t *glyphs = new uint16_t[nGlyphs]; SkPoint *pos = new SkPoint[nGlyphs]; @@ -865,15 +866,7 @@ public: #ifdef USE_MINIKIN Layout layout; - TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface); - layout.setFontCollection(resolvedFace->fFontCollection); - FontStyle style = resolvedFace->fStyle; - char css[256]; - sprintf(css, "font-size: %d; font-weight: %d; font-style: %s", - (int)paint->getTextSize(), - style.getWeight() * 100, - style.getItalic() ? "italic" : "normal"); - layout.setProperties(css); + MinikinUtils::SetLayoutProperties(&layout, paint, flags, typeface); layout.doLayout(textArray + start, count); drawGlyphsToSkia(canvas, paint, &layout, x, y); #else diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp new file mode 100644 index 0000000..041790f --- /dev/null +++ b/core/jni/android/graphics/FontFamily.cpp @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#define LOG_TAG "Minikin" + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> + +#include "SkTypeface.h" +#include "GraphicsJNI.h" +#include <ScopedPrimitiveArray.h> +#include <ScopedUtfChars.h> + +#ifdef USE_MINIKIN +#include <minikin/FontFamily.h> +#include "MinikinSkia.h" +#endif + +namespace android { + +static jlong FontFamily_create(JNIEnv* env, jobject clazz) { +#ifdef USE_MINIKIN + return (jlong)new FontFamily(); +#else + return 0; +#endif +} + +static void FontFamily_unref(JNIEnv* env, jobject clazz, jlong familyPtr) { +#ifdef USE_MINIKIN + FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr); + fontFamily->Unref(); +#endif +} + +static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path) { +#ifdef USE_MINIKIN + NPE_CHECK_RETURN_ZERO(env, path); + ScopedUtfChars str(env, path); + ALOGD("addFont %s", str.c_str()); + SkTypeface* face = SkTypeface::CreateFromFile(str.c_str()); + if (face == NULL) { + ALOGE("addFont failed to create font %s", str.c_str()); + return false; + } + MinikinFont* minikinFont = new MinikinFontSkia(face); + FontFamily* fontFamily = (FontFamily*)familyPtr; + return fontFamily->addFont(minikinFont); +#else + return false; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static JNINativeMethod gFontFamilyMethods[] = { + { "nCreateFamily", "()J", (void*)FontFamily_create }, + { "nUnrefFamily", "(J)V", (void*)FontFamily_unref }, + { "nAddFont", "(JLjava/lang/String;)Z", (void*)FontFamily_addFont }, +}; + +int register_android_graphics_FontFamily(JNIEnv* env) +{ + return android::AndroidRuntime::registerNativeMethods(env, + "android/graphics/FontFamily", + gFontFamilyMethods, NELEM(gFontFamilyMethods)); +} + +} diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index e4c74b2..ed28c24 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -167,6 +167,7 @@ static jfieldID gCanvas_nativeInstanceID; static jclass gPaint_class; static jfieldID gPaint_nativeInstanceID; +static jfieldID gPaint_nativeTypefaceID; static jclass gPicture_class; static jfieldID gPicture_nativeInstanceID; @@ -334,6 +335,16 @@ SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) { return p; } +android::TypefaceImpl* GraphicsJNI::getNativeTypeface(JNIEnv* env, jobject paint) { + SkASSERT(env); + SkASSERT(paint); + SkASSERT(env->IsInstanceOf(paint, gPaint_class)); + jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID); + android::TypefaceImpl* p = reinterpret_cast<android::TypefaceImpl*>(typefaceHandle); + SkASSERT(p); + return p; +} + SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) { SkASSERT(env); @@ -698,6 +709,7 @@ int register_android_graphics_Graphics(JNIEnv* env) gPaint_class = make_globalref(env, "android/graphics/Paint"); gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "J"); + gPaint_nativeTypefaceID = getFieldIDCheck(env, gPaint_class, "mNativeTypeface", "J"); gPicture_class = make_globalref(env, "android/graphics/Picture"); gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "J"); diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index cb154aa..db7b6d9 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -8,6 +8,7 @@ #include "SkPoint.h" #include "SkRect.h" #include "SkImageDecoder.h" +#include "TypefaceImpl.h" #include <jni.h> class SkBitmapRegionDecoder; @@ -46,10 +47,15 @@ public: static SkCanvas* getNativeCanvas(JNIEnv*, jobject canvas); 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 SkRegion* getNativeRegion(JNIEnv*, jobject region); + // Given the 'native' long held by the Rasterizer.java object, return a + // ref to its SkRasterizer* (or NULL). + static SkRasterizer* refNativeRasterizer(jlong rasterizerHandle); + /** Return the corresponding native config from the java Config enum, or kNo_Config if the java object is null. */ diff --git a/core/jni/android/graphics/LayerRasterizer.cpp b/core/jni/android/graphics/LayerRasterizer.cpp deleted file mode 100644 index 79dc275..0000000 --- a/core/jni/android/graphics/LayerRasterizer.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "SkLayerRasterizer.h" -#include <jni.h> - -class SkLayerRasterizerGlue { -public: - static jlong create(JNIEnv* env, jobject) { - return reinterpret_cast<jlong>(new SkLayerRasterizer()); - } - - static void addLayer(JNIEnv* env, jobject, jlong layerHandle, jlong paintHandle, jfloat dx, jfloat dy) { - SkLayerRasterizer* layer = reinterpret_cast<SkLayerRasterizer *>(layerHandle); - const SkPaint* paint = reinterpret_cast<SkPaint *>(paintHandle); - SkASSERT(layer); - SkASSERT(paint); - layer->addLayer(*paint, dx, dy); - } -}; - -///////////////////////////////////////////////////////////////////////////////////////// - -#include <android_runtime/AndroidRuntime.h> - -static JNINativeMethod gLayerRasterizerMethods[] = { - { "nativeConstructor", "()J", (void*)SkLayerRasterizerGlue::create }, - { "nativeAddLayer", "(JJFF)V", (void*)SkLayerRasterizerGlue::addLayer } -}; - -int register_android_graphics_LayerRasterizer(JNIEnv* env) -{ - return android::AndroidRuntime::registerNativeMethods(env, - "android/graphics/LayerRasterizer", - gLayerRasterizerMethods, - SK_ARRAY_COUNT(gLayerRasterizerMethods)); -} diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp index 23af860..cbd20e9 100644 --- a/core/jni/android/graphics/Matrix.cpp +++ b/core/jni/android/graphics/Matrix.cpp @@ -50,10 +50,17 @@ public: SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); return obj->isIdentity() ? JNI_TRUE : JNI_FALSE; } + + static jboolean isAffine(JNIEnv* env, jobject clazz, jlong objHandle) { + SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); + return obj->asAffine(NULL) ? JNI_TRUE : JNI_FALSE; + } + static jboolean rectStaysRect(JNIEnv* env, jobject clazz, jlong objHandle) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE; } + static void reset(JNIEnv* env, jobject clazz, jlong objHandle) { SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle); obj->reset(); @@ -302,6 +309,7 @@ static JNINativeMethod methods[] = { {"finalizer", "(J)V", (void*) SkMatrixGlue::finalizer}, {"native_create","(J)J", (void*) SkMatrixGlue::create}, {"native_isIdentity","(J)Z", (void*) SkMatrixGlue::isIdentity}, + {"native_isAffine","(J)Z", (void*) SkMatrixGlue::isAffine}, {"native_rectStaysRect","(J)Z", (void*) SkMatrixGlue::rectStaysRect}, {"native_reset","(J)V", (void*) SkMatrixGlue::reset}, {"native_set","(JJ)V", (void*) SkMatrixGlue::set}, diff --git a/core/jni/android/graphics/MinikinSkia.cpp b/core/jni/android/graphics/MinikinSkia.cpp index 622c935..243fa10 100644 --- a/core/jni/android/graphics/MinikinSkia.cpp +++ b/core/jni/android/graphics/MinikinSkia.cpp @@ -16,7 +16,6 @@ #include <SkTypeface.h> #include <SkPaint.h> -#include <SkFP.h> #define LOG_TAG "Minikin" #include <cutils/log.h> @@ -44,19 +43,37 @@ bool MinikinFontSkia::GetGlyph(uint32_t codepoint, uint32_t *glyph) const { return !!glyph; } +static void MinikinFontSkia_SetSkiaPaint(SkTypeface* typeface, SkPaint* skPaint, const MinikinPaint& paint) { + skPaint->setTypeface(typeface); + skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + // TODO: set more paint parameters from Minikin + skPaint->setTextSize(paint.size); +} + float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id, const MinikinPaint &paint) const { - SkPaint skpaint; - skpaint.setTypeface(mTypeface); - skpaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - // TODO: set more paint parameters from Minikin - skpaint.setTextSize(paint.size); + SkPaint skPaint; uint16_t glyph16 = glyph_id; SkScalar skWidth; + MinikinFontSkia_SetSkiaPaint(mTypeface, &skPaint, paint); + skPaint.getTextWidths(&glyph16, sizeof(glyph16), &skWidth, NULL); +#ifdef VERBOSE + ALOGD("width for typeface %d glyph %d = %f", mTypeface->uniqueID(), glyph_id, skWidth); +#endif + return skWidth; +} + +void MinikinFontSkia::GetBounds(MinikinRect* bounds, uint32_t glyph_id, + const MinikinPaint& paint) const { + SkPaint skPaint; + uint16_t glyph16 = glyph_id; SkRect skBounds; - skpaint.getTextWidths(&glyph16, sizeof(glyph16), &skWidth, &skBounds); - // TODO: get bounds information - return SkScalarToFP(skWidth); + MinikinFontSkia_SetSkiaPaint(mTypeface, &skPaint, paint); + skPaint.getTextWidths(&glyph16, sizeof(glyph16), NULL, &skBounds); + bounds->mLeft = skBounds.fLeft; + bounds->mTop = skBounds.fTop; + bounds->mRight = skBounds.fRight; + bounds->mBottom = skBounds.fBottom; } bool MinikinFontSkia::GetTable(uint32_t tag, uint8_t *buf, size_t *size) { diff --git a/core/jni/android/graphics/MinikinSkia.h b/core/jni/android/graphics/MinikinSkia.h index 0edb557..7a8954d 100644 --- a/core/jni/android/graphics/MinikinSkia.h +++ b/core/jni/android/graphics/MinikinSkia.h @@ -27,6 +27,9 @@ public: float GetHorizontalAdvance(uint32_t glyph_id, const MinikinPaint &paint) const; + void GetBounds(MinikinRect* bounds, uint32_t glyph_id, + const MinikinPaint &paint) const; + // If buf is NULL, just update size bool GetTable(uint32_t tag, uint8_t *buf, size_t *size); @@ -36,7 +39,6 @@ public: private: SkTypeface *mTypeface; - }; } // namespace android
\ No newline at end of file diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp new file mode 100644 index 0000000..ee04d6f --- /dev/null +++ b/core/jni/android/graphics/MinikinUtils.cpp @@ -0,0 +1,39 @@ +/* + * 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 "SkPaint.h" +#include "minikin/Layout.h" +#include "TypefaceImpl.h" + +#include "MinikinUtils.h" + +namespace android { + +void MinikinUtils::SetLayoutProperties(Layout* layout, SkPaint* paint, int flags, + TypefaceImpl* typeface) { + TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface); + layout->setFontCollection(resolvedFace->fFontCollection); + FontStyle style = resolvedFace->fStyle; + char css[256]; + sprintf(css, "font-size: %d; font-weight: %d; font-style: %s; -minikin-bidi: %d", + (int)paint->getTextSize(), + style.getWeight() * 100, + style.getItalic() ? "italic" : "normal", + flags); + layout->setProperties(css); +} + +}
\ No newline at end of file diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h new file mode 100644 index 0000000..42f5e2f --- /dev/null +++ b/core/jni/android/graphics/MinikinUtils.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/** + * Utilities for making Minikin work, especially from existing objects like + * SkPaint and so on. + **/ + + // TODO: does this really need to be separate from MinikinSkia? + +#ifndef ANDROID_MINIKIN_UTILS_H +#define ANDROID_MINIKIN_UTILS_H + +namespace android { + +class MinikinUtils { +public: + static void SetLayoutProperties(Layout* layout, SkPaint* paint, int flags, + TypefaceImpl* face); +}; + +} // namespace android + +#endif // ANDROID_MINIKIN_UTILS_H diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 22c17dd..43e80dc 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -36,6 +36,12 @@ #include "utils/Blur.h" #include "TextLayout.h" +#ifdef USE_MINIKIN +#include <minikin/Layout.h> +#include "MinikinSkia.h" +#include "MinikinUtils.h" +#endif + // temporary for debugging #include <Caches.h> #include <utils/Log.h> @@ -100,16 +106,33 @@ public: *dst = *src; } + // Equivalent to the Java Paint's FILTER_BITMAP_FLAG. + static const uint32_t sFilterBitmapFlag = 0x02; + static jint getFlags(JNIEnv* env, jobject paint) { NPE_CHECK_RETURN_ZERO(env, paint); - int result; - result = GraphicsJNI::getNativePaint(env, paint)->getFlags(); + SkPaint* nativePaint = GraphicsJNI::getNativePaint(env, paint); + uint32_t result = nativePaint->getFlags(); + result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away. + if (nativePaint->getFilterLevel() != SkPaint::kNone_FilterLevel) { + result |= sFilterBitmapFlag; + } return static_cast<jint>(result); } static void setFlags(JNIEnv* env, jobject paint, jint flags) { NPE_CHECK_RETURN_VOID(env, paint); - GraphicsJNI::getNativePaint(env, paint)->setFlags(flags); + SkPaint* nativePaint = GraphicsJNI::getNativePaint(env, paint); + // Instead of modifying 0x02, change the filter level. + nativePaint->setFilterLevel(flags & sFilterBitmapFlag + ? SkPaint::kLow_FilterLevel + : SkPaint::kNone_FilterLevel); + // Don't pass through filter flag, which is no longer stored in paint's flags. + flags &= ~sFilterBitmapFlag; + // Use the existing value for 0x02. + const uint32_t existing0x02Flag = nativePaint->getFlags() & sFilterBitmapFlag; + flags |= existing0x02Flag; + nativePaint->setFlags(flags); } static jint getHinting(JNIEnv* env, jobject paint) { @@ -292,7 +315,7 @@ public: static jlong setRasterizer(JNIEnv* env, jobject clazz, jlong objHandle, jlong rasterizerHandle) { SkPaint* obj = reinterpret_cast<SkPaint*>(objHandle); - SkRasterizer* rasterizer = reinterpret_cast<SkRasterizer*>(rasterizerHandle); + SkAutoTUnref<SkRasterizer> rasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle)); return reinterpret_cast<jlong>(obj->setRasterizer(rasterizer)); } @@ -494,8 +517,16 @@ public: const jchar* textArray = env->GetCharArrayElements(text, NULL); jfloat result = 0; +#ifdef USE_MINIKIN + Layout layout; + TypefaceImpl* typeface = GraphicsJNI::getNativeTypeface(env, jpaint); + MinikinUtils::SetLayoutProperties(&layout, paint, bidiFlags, typeface); + layout.doLayout(textArray + index, count); + result = layout.getAdvance(); +#else TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength, bidiFlags, NULL /* dont need all advances */, &result); +#endif env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return result; @@ -520,8 +551,16 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); jfloat width = 0; +#ifdef USE_MINIKIN + Layout layout; + TypefaceImpl* typeface = GraphicsJNI::getNativeTypeface(env, jpaint); + MinikinUtils::SetLayoutProperties(&layout, paint, bidiFlags, typeface); + layout.doLayout(textArray + start, count); + width = layout.getAdvance(); +#else TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength, bidiFlags, NULL /* dont need all advances */, &width); +#endif env->ReleaseStringChars(text, textArray); return width; @@ -540,15 +579,23 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); jfloat width = 0; +#ifdef USE_MINIKIN + Layout layout; + TypefaceImpl* typeface = GraphicsJNI::getNativeTypeface(env, jpaint); + MinikinUtils::SetLayoutProperties(&layout, paint, bidiFlags, typeface); + layout.doLayout(textArray, textLength); + width = layout.getAdvance(); +#else TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength, bidiFlags, NULL /* dont need all advances */, &width); +#endif env->ReleaseStringChars(text, textArray); return width; } - static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths, - jint bidiFlags) { + static int dotextwidths(JNIEnv* env, SkPaint* paint, TypefaceImpl* typeface, const jchar text[], int count, + jfloatArray widths, jint bidiFlags) { NPE_CHECK_RETURN_ZERO(env, paint); NPE_CHECK_RETURN_ZERO(env, text); @@ -568,27 +615,36 @@ public: AutoJavaFloatArray autoWidths(env, widths, count); jfloat* widthsArray = autoWidths.ptr(); +#ifdef USE_MINIKIN + Layout layout; + MinikinUtils::SetLayoutProperties(&layout, paint, bidiFlags, typeface); + layout.doLayout(text, count); + layout.getAdvances(widthsArray); +#else TextLayout::getTextRunAdvances(paint, text, 0, count, count, bidiFlags, widthsArray, NULL /* dont need totalAdvance */); +#endif return count; } - static jint getTextWidths___CIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text, + static jint getTextWidths___CIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray text, jint index, jint count, jint bidiFlags, jfloatArray widths) { SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); const jchar* textArray = env->GetCharArrayElements(text, NULL); - count = dotextwidths(env, paint, textArray + index, count, widths, bidiFlags); + count = dotextwidths(env, paint, typeface, textArray + index, count, widths, bidiFlags); env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return count; } - static jint getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text, + static jint getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring text, jint start, jint end, jint bidiFlags, jfloatArray widths) { SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); const jchar* textArray = env->GetStringChars(text, NULL); - int count = dotextwidths(env, paint, textArray + start, end - start, widths, bidiFlags); + int count = dotextwidths(env, paint, typeface, textArray + start, end - start, widths, bidiFlags); env->ReleaseStringChars(text, textArray); return count; } @@ -634,7 +690,7 @@ public: return count; } - static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text, + static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, TypefaceImpl* typeface, const jchar *text, jint start, jint count, jint contextCount, jint flags, jfloatArray advances, jint advancesIndex) { NPE_CHECK_RETURN_ZERO(env, paint); @@ -657,8 +713,16 @@ public: jfloat* advancesArray = new jfloat[count]; jfloat totalAdvance = 0; +#ifdef USE_MINIKIN + Layout layout; + MinikinUtils::SetLayoutProperties(&layout, paint, flags, typeface); + layout.doLayout(text + start, count); + layout.getAdvances(advancesArray); + totalAdvance = layout.getAdvance(); +#else TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags, advancesArray, &totalAdvance); +#endif if (advances != NULL) { env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray); @@ -668,22 +732,26 @@ public: } static jfloat getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle, + jlong typefaceHandle, jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, jint flags, jfloatArray advances, jint advancesIndex) { SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); jchar* textArray = env->GetCharArrayElements(text, NULL); - jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex, + jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextIndex, index - contextIndex, count, contextCount, flags, advances, advancesIndex); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); return result; } static jfloat getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle, + jlong typefaceHandle, jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags, jfloatArray advances, jint advancesIndex) { SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); const jchar* textArray = env->GetStringChars(text, NULL); - jfloat result = doTextRunAdvances(env, paint, textArray + contextStart, + jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextStart, start - contextStart, end - start, contextEnd - contextStart, flags, advances, advancesIndex); env->ReleaseStringChars(text, textArray); @@ -954,11 +1022,11 @@ static JNINativeMethod methods[] = { {"native_measureText","(Ljava/lang/String;III)F", (void*) SkPaintGlue::measureText_StringIII}, {"native_breakText","([CIIFI[F)I", (void*) SkPaintGlue::breakTextC}, {"native_breakText","(Ljava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS}, - {"native_getTextWidths","(J[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F}, - {"native_getTextWidths","(JLjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F}, - {"native_getTextRunAdvances","(J[CIIIII[FI)F", + {"native_getTextWidths","(JJ[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F}, + {"native_getTextWidths","(JJLjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F}, + {"native_getTextRunAdvances","(JJ[CIIIII[FI)F", (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI}, - {"native_getTextRunAdvances","(JLjava/lang/String;IIIII[FI)F", + {"native_getTextRunAdvances","(JJLjava/lang/String;IIIII[FI)F", (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI}, diff --git a/core/jni/android/graphics/Rasterizer.cpp b/core/jni/android/graphics/Rasterizer.cpp index b6450d0..a05d19b 100644 --- a/core/jni/android/graphics/Rasterizer.cpp +++ b/core/jni/android/graphics/Rasterizer.cpp @@ -22,30 +22,82 @@ #include "jni.h" #include "GraphicsJNI.h" +#include "SkLayerRasterizer.h" #include <android_runtime/AndroidRuntime.h> -#include "SkRasterizer.h" +// Rasterizer.java holds a pointer (jlong) to this guy +class NativeRasterizer { +public: + NativeRasterizer() {} + virtual ~NativeRasterizer() {} + + // Can return NULL, or a ref to the skia rasterizer. + virtual SkRasterizer* refRasterizer() { return NULL; } +}; + +class NativeLayerRasterizer : public NativeRasterizer { +public: + SkLayerRasterizer::Builder fBuilder; + + virtual SkRasterizer* refRasterizer() { + return fBuilder.snapshotRasterizer(); + } +}; + +SkRasterizer* GraphicsJNI::refNativeRasterizer(jlong rasterizerHandle) { + NativeRasterizer* nr = reinterpret_cast<NativeRasterizer*>(rasterizerHandle); + return nr ? nr->refRasterizer() : NULL; +} + +/////////////////////////////////////////////////////////////////////////////// namespace android { class SkRasterizerGlue { public: - static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) { - SkRasterizer* obj = reinterpret_cast<SkRasterizer *>(objHandle); - SkSafeUnref(obj); + delete reinterpret_cast<NativeRasterizer *>(objHandle); } }; -static JNINativeMethod methods[] = { +static JNINativeMethod gRasterizerMethods[] = { {"finalizer", "(J)V", (void*) SkRasterizerGlue::finalizer} }; int register_android_graphics_Rasterizer(JNIEnv* env) { - int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Rasterizer", methods, - sizeof(methods) / sizeof(methods[0])); + int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Rasterizer", gRasterizerMethods, + sizeof(gRasterizerMethods) / sizeof(gRasterizerMethods[0])); return result; } +class SkLayerRasterizerGlue { +public: + static jlong create(JNIEnv* env, jobject) { + return reinterpret_cast<jlong>(new NativeLayerRasterizer); + } + + static void addLayer(JNIEnv* env, jobject, jlong layerHandle, jlong paintHandle, jfloat dx, jfloat dy) { + NativeLayerRasterizer* nr = reinterpret_cast<NativeLayerRasterizer *>(layerHandle); + const SkPaint* paint = reinterpret_cast<SkPaint *>(paintHandle); + SkASSERT(nr); + SkASSERT(paint); + nr->fBuilder.addLayer(*paint, dx, dy); + } +}; + +static JNINativeMethod gLayerRasterizerMethods[] = { + { "nativeConstructor", "()J", (void*)SkLayerRasterizerGlue::create }, + { "nativeAddLayer", "(JJFF)V", (void*)SkLayerRasterizerGlue::addLayer } +}; + +int register_android_graphics_LayerRasterizer(JNIEnv* env) +{ + return android::AndroidRuntime::registerNativeMethods(env, + "android/graphics/LayerRasterizer", + gLayerRasterizerMethods, + SK_ARRAY_COUNT(gLayerRasterizerMethods)); } + +} + diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index 9279758..bf58918 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -367,6 +367,7 @@ void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars, bool forceLTR = false; bool forceRTL = false; + ALOGD("computeValues dirFlags=%d", dirFlags); switch (dirFlags & kBidi_Mask) { case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index a349a7f..b20c246 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -18,6 +18,7 @@ #include <android_runtime/AndroidRuntime.h> #include "GraphicsJNI.h" +#include <ScopedPrimitiveArray.h> #include "SkStream.h" #include "SkTypeface.h" #include "TypefaceImpl.h" @@ -62,7 +63,7 @@ static jlong Typeface_create(JNIEnv* env, jobject, jstring name, } static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) { - SkTypeface* family = reinterpret_cast<SkTypeface*>(familyHandle); + TypefaceImpl* family = reinterpret_cast<TypefaceImpl*>(familyHandle); TypefaceImpl* face = TypefaceImpl_createFromTypeface(family, (SkTypeface::Style)style); // Try to find the closest matching font, using the standard heuristic if (NULL == face) { @@ -114,6 +115,16 @@ static jlong Typeface_createFromFile(JNIEnv* env, jobject, jstring jpath) { return reinterpret_cast<jlong>(TypefaceImpl_createFromFile(str.c_str())); } +static jlong Typeface_createFromArray(JNIEnv *env, jobject, jlongArray familyArray) { + ScopedLongArrayRO families(env, familyArray); + return reinterpret_cast<jlong>(TypefaceImpl_createFromFamilies(families.get(), families.size())); +} + +static void Typeface_setDefault(JNIEnv *env, jobject, jlong faceHandle) { + TypefaceImpl* face = reinterpret_cast<TypefaceImpl*>(faceHandle); + return TypefaceImpl_setDefault(face); +} + /////////////////////////////////////////////////////////////////////////////// static JNINativeMethod gTypefaceMethods[] = { @@ -125,6 +136,9 @@ static JNINativeMethod gTypefaceMethods[] = { (void*)Typeface_createFromAsset }, { "nativeCreateFromFile", "(Ljava/lang/String;)J", (void*)Typeface_createFromFile }, + { "nativeCreateFromArray", "([J)J", + (void*)Typeface_createFromArray }, + { "nativeSetDefault", "(J)V", (void*)Typeface_setDefault }, }; int register_android_graphics_Typeface(JNIEnv* env) diff --git a/core/jni/android/graphics/TypefaceImpl.cpp b/core/jni/android/graphics/TypefaceImpl.cpp index f6d3a6e..958cd85 100644 --- a/core/jni/android/graphics/TypefaceImpl.cpp +++ b/core/jni/android/graphics/TypefaceImpl.cpp @@ -20,6 +20,10 @@ * being, that choice is hidden under the USE_MINIKIN compile-time flag. */ +#define LOG_TAG "TypefaceImpl" + +#include "jni.h" // for jlong, remove when being passed proper type + #include "SkStream.h" #include "SkTypeface.h" @@ -48,48 +52,49 @@ static FontStyle styleFromSkiaStyle(SkTypeface::Style skiaStyle) { return FontStyle(weight, italic); } -TypefaceImpl* gDefaultTypeface; +TypefaceImpl* gDefaultTypeface = NULL; pthread_once_t gDefaultTypefaceOnce = PTHREAD_ONCE_INIT; -// TODO: this currently builds a font collection from hardcoded paths. -// It will get replaced by an implementation that parses the XML files. +// This installs a default typeface (from a hardcoded path) that allows +// layouts to work (not crash on null pointer) before the default +// typeface is set. +// TODO: investigate why layouts are being created before Typeface.java +// class initialization. static FontCollection *makeFontCollection() { std::vector<FontFamily *>typefaces; const char *fns[] = { "/system/fonts/Roboto-Regular.ttf", - "/system/fonts/Roboto-Italic.ttf", - "/system/fonts/Roboto-BoldItalic.ttf", - "/system/fonts/Roboto-Light.ttf", - "/system/fonts/Roboto-Thin.ttf", - "/system/fonts/Roboto-Bold.ttf", - "/system/fonts/Roboto-ThinItalic.ttf", - "/system/fonts/Roboto-LightItalic.ttf" }; FontFamily *family = new FontFamily(); for (size_t i = 0; i < sizeof(fns)/sizeof(fns[0]); i++) { const char *fn = fns[i]; + ALOGD("makeFontCollection adding %s", fn); SkTypeface *skFace = SkTypeface::CreateFromFile(fn); - MinikinFont *font = new MinikinFontSkia(skFace); - family->addFont(font); + if (skFace != NULL) { + MinikinFont *font = new MinikinFontSkia(skFace); + family->addFont(font); + font->Unref(); + } else { + ALOGE("failed to create font %s", fn); + } } typefaces.push_back(family); - family = new FontFamily(); - const char *fn = "/system/fonts/NotoSansDevanagari-Regular.ttf"; - SkTypeface *skFace = SkTypeface::CreateFromFile(fn); - MinikinFont *font = new MinikinFontSkia(skFace); - family->addFont(font); - typefaces.push_back(family); - - return new FontCollection(typefaces); + FontCollection *result = new FontCollection(typefaces); + family->Unref(); + return result; } static void getDefaultTypefaceOnce() { Layout::init(); - gDefaultTypeface = new TypefaceImpl; - gDefaultTypeface->fFontCollection = makeFontCollection(); - gDefaultTypeface->fStyle = FontStyle(); + if (gDefaultTypeface == NULL) { + // We expect the client to set a default typeface, but provide a + // default so we can make progress before that happens. + gDefaultTypeface = new TypefaceImpl; + gDefaultTypeface->fFontCollection = makeFontCollection(); + gDefaultTypeface->fStyle = FontStyle(); + } } TypefaceImpl* TypefaceImpl_resolveDefault(TypefaceImpl* src) { @@ -106,19 +111,25 @@ TypefaceImpl* TypefaceImpl_createFromTypeface(TypefaceImpl* src, SkTypeface::Sty TypefaceImpl* result = new TypefaceImpl; if (result != 0) { result->fFontCollection = resolvedFace->fFontCollection; + result->fFontCollection->Ref(); result->fStyle = styleFromSkiaStyle(style); } return result; } static TypefaceImpl* createFromSkTypeface(SkTypeface* typeface) { + if (typeface == NULL) { + return NULL; + } MinikinFont* minikinFont = new MinikinFontSkia(typeface); std::vector<FontFamily *> typefaces; FontFamily* family = new FontFamily(); family->addFont(minikinFont); + minikinFont->Unref(); typefaces.push_back(family); TypefaceImpl* result = new TypefaceImpl; result->fFontCollection = new FontCollection(typefaces); + family->Unref(); result->fStyle = FontStyle(); // TODO: improve return result; } @@ -146,7 +157,21 @@ TypefaceImpl* TypefaceImpl_createFromAsset(Asset* asset) { return createFromSkTypeface(face); } +TypefaceImpl* TypefaceImpl_createFromFamilies(const jlong* families, size_t size) { + ALOGD("createFromFamilies size=%d", size); + std::vector<FontFamily *>familyVec; + for (size_t i = 0; i < size; i++) { + FontFamily* family = reinterpret_cast<FontFamily*>(families[i]); + familyVec.push_back(family); + } + TypefaceImpl* result = new TypefaceImpl; + result->fFontCollection = new FontCollection(familyVec); + result->fStyle = FontStyle(); // TODO: improve + return result; +} + void TypefaceImpl_unref(TypefaceImpl* face) { + face->fFontCollection->Unref(); delete face; } @@ -159,6 +184,10 @@ int TypefaceImpl_getStyle(TypefaceImpl* face) { return result; } +void TypefaceImpl_setDefault(TypefaceImpl* face) { + gDefaultTypeface = face; +} + #else // USE_MINIKIN /* Just use SkTypeface instead. */ @@ -189,6 +218,11 @@ TypefaceImpl* TypefaceImpl_createFromAsset(Asset* asset) { return face; } +TypefaceImpl* TypefaceImpl_createFromFamilies(const jlong* families, size_t size) { + // Should never be called in non-Minikin builds + return 0; +} + void TypefaceImpl_unref(TypefaceImpl* face) { SkSafeUnref(face); } @@ -197,6 +231,9 @@ int TypefaceImpl_getStyle(TypefaceImpl* face) { return face->style(); } +void TypefaceImpl_setDefault(TypefaceImpl* face) { +} + #endif // USE_MINIKIN } diff --git a/core/jni/android/graphics/TypefaceImpl.h b/core/jni/android/graphics/TypefaceImpl.h index 4c51bec..4e92bce 100644 --- a/core/jni/android/graphics/TypefaceImpl.h +++ b/core/jni/android/graphics/TypefaceImpl.h @@ -18,6 +18,8 @@ #ifndef ANDROID_TYPEFACE_IMPL_H #define ANDROID_TYPEFACE_IMPL_H +#include "jni.h" // for jlong, eventually remove +#include "SkTypeface.h" #include <androidfw/AssetManager.h> #ifdef USE_MINIKIN @@ -51,10 +53,16 @@ TypefaceImpl* TypefaceImpl_createFromFile(const char* filename); TypefaceImpl* TypefaceImpl_createFromAsset(Asset* asset); +// When we remove the USE_MINIKIN ifdef, probably a good idea to move the casting +// (from jlong to FontFamily*) to the caller in Typeface.cpp. +TypefaceImpl* TypefaceImpl_createFromFamilies(const jlong* families, size_t size); + void TypefaceImpl_unref(TypefaceImpl* face); int TypefaceImpl_getStyle(TypefaceImpl* face); +void TypefaceImpl_setDefault(TypefaceImpl* face); + } #endif // ANDROID_TYPEFACE_IMPL_H
\ No newline at end of file diff --git a/core/jni/android/graphics/pdf/PdfRenderer.cpp b/core/jni/android/graphics/pdf/PdfRenderer.cpp new file mode 100644 index 0000000..15de24a --- /dev/null +++ b/core/jni/android/graphics/pdf/PdfRenderer.cpp @@ -0,0 +1,279 @@ +/* + * 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 "jni.h" +#include "JNIHelp.h" +#include "GraphicsJNI.h" +#include "SkBitmap.h" +#include "SkMatrix.h" +#include "fpdfview.h" +#include "fsdk_rendercontext.h" + +#include <android_runtime/AndroidRuntime.h> +#include <vector> +#include <utils/Log.h> +#include <unistd.h> +#include <sys/types.h> +#include <unistd.h> + +namespace android { + +static const int RENDER_MODE_FOR_DISPLAY = 1; +static const int RENDER_MODE_FOR_PRINT = 2; + +static struct { + jfieldID x; + jfieldID y; +} gPointClassInfo; + +static Mutex sLock; + +static int sUnmatchedInitRequestCount = 0; + +static void initializeLibraryIfNeeded() { + Mutex::Autolock _l(sLock); + if (sUnmatchedInitRequestCount == 0) { + FPDF_InitLibrary(NULL); + } + sUnmatchedInitRequestCount++; +} + +static void destroyLibraryIfNeeded() { + Mutex::Autolock _l(sLock); + sUnmatchedInitRequestCount--; + if (sUnmatchedInitRequestCount == 0) { + FPDF_DestroyLibrary(); + } +} + +static int getBlock(void* param, unsigned long position, unsigned char* outBuffer, + unsigned long size) { + const int fd = reinterpret_cast<intptr_t>(param); + const int readCount = pread(fd, outBuffer, size, position); + if (readCount < 0) { + ALOGE("Cannot read from file descriptor. Error:%d", errno); + return 0; + } + return 1; +} + +static jlong nativeCreate(JNIEnv* env, jclass thiz, jint fd, jlong size) { + initializeLibraryIfNeeded(); + + FPDF_FILEACCESS loader; + loader.m_FileLen = size; + loader.m_Param = reinterpret_cast<void*>(intptr_t(fd)); + loader.m_GetBlock = &getBlock; + + FPDF_DOCUMENT document = FPDF_LoadCustomDocument(&loader, NULL); + + if (!document) { + const long error = FPDF_GetLastError(); + jniThrowException(env, "java/io/IOException", + "cannot create document. Error:" + error); + destroyLibraryIfNeeded(); + return -1; + } + + return reinterpret_cast<jlong>(document); +} + +static jlong nativeOpenPageAndGetSize(JNIEnv* env, jclass thiz, jlong documentPtr, + jint pageIndex, jobject outSize) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + + FPDF_PAGE page = FPDF_LoadPage(document, pageIndex); + + if (!page) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot load page"); + return -1; + } + + double width = 0; + double height = 0; + + const int result = FPDF_GetPageSizeByIndex(document, pageIndex, &width, &height); + + if (!result) { + jniThrowException(env, "java/lang/IllegalStateException", + "cannot get page size"); + return -1; + } + + env->SetIntField(outSize, gPointClassInfo.x, width); + env->SetIntField(outSize, gPointClassInfo.y, height); + + return reinterpret_cast<jlong>(page); +} + +static void nativeClosePage(JNIEnv* env, jclass thiz, jlong pagePtr) { + FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr); + FPDF_ClosePage(page); +} + +static void nativeClose(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + FPDF_CloseDocument(document); + destroyLibraryIfNeeded(); +} + +static jint nativeGetPageCount(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + return FPDF_GetPageCount(document); +} + +static jboolean nativeScaleForPrinting(JNIEnv* env, jclass thiz, jlong documentPtr) { + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + return FPDF_VIEWERREF_GetPrintScaling(document); +} + +static void DropContext(void* data) { + delete (CRenderContext*) data; +} + +static void renderPageBitmap(FPDF_BITMAP bitmap, FPDF_PAGE page, int destLeft, int destTop, + int destRight, int destBottom, SkMatrix* transform, int flags) { + // Note: this code ignores the currently unused RENDER_NO_NATIVETEXT, + // FPDF_RENDER_LIMITEDIMAGECACHE, FPDF_RENDER_FORCEHALFTONE, FPDF_GRAYSCALE, + // and FPDF_ANNOT flags. To add support for that refer to FPDF_RenderPage_Retail + // in fpdfview.cpp + + CRenderContext* pContext = FX_NEW CRenderContext; + + CPDF_Page* pPage = (CPDF_Page*) page; + pPage->SetPrivateData((void*) 1, pContext, DropContext); + + CFX_FxgeDevice* fxgeDevice = FX_NEW CFX_FxgeDevice; + pContext->m_pDevice = fxgeDevice; + + // Reverse the bytes (last argument TRUE) since the Android + // format is ARGB while the renderer uses BGRA internally. + fxgeDevice->Attach((CFX_DIBitmap*) bitmap, 0, TRUE); + + CPDF_RenderOptions* renderOptions = pContext->m_pOptions; + + if (!renderOptions) { + renderOptions = FX_NEW CPDF_RenderOptions; + pContext->m_pOptions = renderOptions; + } + + if (flags & FPDF_LCD_TEXT) { + renderOptions->m_Flags |= RENDER_CLEARTYPE; + } else { + renderOptions->m_Flags &= ~RENDER_CLEARTYPE; + } + + const CPDF_OCContext::UsageType usage = (flags & FPDF_PRINTING) + ? CPDF_OCContext::Print : CPDF_OCContext::View; + + renderOptions->m_AddFlags = flags >> 8; + renderOptions->m_pOCContext = new CPDF_OCContext(pPage->m_pDocument, usage); + + fxgeDevice->SaveState(); + + FX_RECT clip; + clip.left = destLeft; + clip.right = destRight; + clip.top = destTop; + clip.bottom = destBottom; + fxgeDevice->SetClip_Rect(&clip); + + CPDF_RenderContext* pageContext = FX_NEW CPDF_RenderContext; + pContext->m_pContext = pageContext; + pageContext->Create(pPage); + + CFX_AffineMatrix matrix; + if (!transform) { + pPage->GetDisplayMatrix(matrix, destLeft, destTop, destRight - destLeft, + destBottom - destTop, 0); + } else { + // PDF's coordinate system origin is left-bottom while + // in graphics it is the top-left, so remap the origin. + matrix.Set(1, 0, 0, -1, 0, pPage->GetPageHeight()); + matrix.Scale(transform->getScaleX(), transform->getScaleY()); + matrix.Rotate(transform->getSkewX(), transform->getSkewY()); + matrix.Translate(transform->getTranslateX(), transform->getTranslateY()); + } + pageContext->AppendObjectList(pPage, &matrix); + + pContext->m_pRenderer = FX_NEW CPDF_ProgressiveRenderer; + pContext->m_pRenderer->Start(pageContext, fxgeDevice, renderOptions, NULL); + + fxgeDevice->RestoreState(); + + pPage->RemovePrivateData((void*) 1); + + delete pContext; +} + +static void nativeRenderPage(JNIEnv* env, jclass thiz, jlong documentPtr, jlong pagePtr, + jlong bitmapPtr, jint destLeft, jint destTop, jint destRight, jint destBottom, + jlong matrixPtr, jint renderMode) { + + FPDF_DOCUMENT document = reinterpret_cast<FPDF_DOCUMENT>(documentPtr); + FPDF_PAGE page = reinterpret_cast<FPDF_PAGE>(pagePtr); + SkBitmap* skBitmap = reinterpret_cast<SkBitmap*>(bitmapPtr); + SkMatrix* skMatrix = reinterpret_cast<SkMatrix*>(matrixPtr); + + skBitmap->lockPixels(); + + const int stride = skBitmap->width() * 4; + + FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(skBitmap->width(), skBitmap->height(), + FPDFBitmap_BGRA, skBitmap->getPixels(), stride); + + if (!bitmap) { + ALOGE("Erorr creating bitmap"); + return; + } + + int renderFlags = 0; + if (renderMode == RENDER_MODE_FOR_DISPLAY) { + renderFlags |= FPDF_LCD_TEXT; + } else if (renderMode == RENDER_MODE_FOR_PRINT) { + renderFlags |= FPDF_PRINTING; + } + + renderPageBitmap(bitmap, page, destLeft, destTop, destRight, + destBottom, skMatrix, renderFlags); + + skBitmap->unlockPixels(); +} + +static JNINativeMethod gPdfRenderer_Methods[] = { + {"nativeCreate", "(IJ)J", (void*) nativeCreate}, + {"nativeClose", "(J)V", (void*) nativeClose}, + {"nativeGetPageCount", "(J)I", (void*) nativeGetPageCount}, + {"nativeScaleForPrinting", "(J)Z", (void*) nativeScaleForPrinting}, + {"nativeRenderPage", "(JJJIIIIJI)V", (void*) nativeRenderPage}, + {"nativeOpenPageAndGetSize", "(JILandroid/graphics/Point;)J", (void*) nativeOpenPageAndGetSize}, + {"nativeClosePage", "(J)V", (void*) nativeClosePage} +}; + +int register_android_graphics_pdf_PdfRenderer(JNIEnv* env) { + int result = android::AndroidRuntime::registerNativeMethods( + env, "android/graphics/pdf/PdfRenderer", gPdfRenderer_Methods, + NELEM(gPdfRenderer_Methods)); + + jclass clazz = env->FindClass("android/graphics/Point"); + gPointClassInfo.x = env->GetFieldID(clazz, "x", "I"); + gPointClassInfo.y = env->GetFieldID(clazz, "y", "I"); + + return result; +}; + +}; diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 463a0a8..5fcb5f3 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -201,22 +201,6 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, { ALOGV("sampleRate=%d, audioFormat(from Java)=%d, channel mask=%x, buffSize=%d", sampleRateInHertz, audioFormat, javaChannelMask, buffSizeInBytes); - uint32_t afSampleRate; - size_t afFrameCount; - - status_t status = AudioSystem::getOutputFrameCount(&afFrameCount, - (audio_stream_type_t) streamType); - if (status != NO_ERROR) { - ALOGE("Error %d creating AudioTrack: Could not get AudioSystem frame count " - "for stream type %d.", status, streamType); - return (jint) AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; - } - status = AudioSystem::getOutputSamplingRate(&afSampleRate, (audio_stream_type_t) streamType); - if (status != NO_ERROR) { - ALOGE("Error %d creating AudioTrack: Could not get AudioSystem sampling rate " - "for stream type %d.", status, streamType); - return (jint) AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM; - } // Java channel masks don't map directly to the native definition, but it's a simple shift // to skip the two deprecated channel configurations "default" and "mono". @@ -229,23 +213,8 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, uint32_t channelCount = popcount(nativeChannelMask); - // check the stream type - audio_stream_type_t atStreamType; - switch (streamType) { - case AUDIO_STREAM_VOICE_CALL: - case AUDIO_STREAM_SYSTEM: - case AUDIO_STREAM_RING: - case AUDIO_STREAM_MUSIC: - case AUDIO_STREAM_ALARM: - case AUDIO_STREAM_NOTIFICATION: - case AUDIO_STREAM_BLUETOOTH_SCO: - case AUDIO_STREAM_DTMF: - atStreamType = (audio_stream_type_t) streamType; - break; - default: - ALOGE("Error creating AudioTrack: unknown stream type %d.", streamType); - return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE; - } + // stream type already checked in Java + audio_stream_type_t atStreamType = (audio_stream_type_t) streamType; // check the format. // This function was called from Java, so we compare the format against the Java constants @@ -305,6 +274,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, lpJniStorage->mCallbackData.busy = false; // initialize the native AudioTrack object + status_t status = NO_ERROR; switch (memoryMode) { case MODE_STREAM: diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 662af89..d82fc96 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -697,6 +697,38 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, jniThrowException(env, "java/lang/RuntimeException", "Not allowed to write file descriptors here"); break; + case -EBADF: + jniThrowException(env, "java/lang/RuntimeException", + "Bad file descriptor"); + break; + case -ENFILE: + jniThrowException(env, "java/lang/RuntimeException", + "File table overflow"); + break; + case -EMFILE: + jniThrowException(env, "java/lang/RuntimeException", + "Too many open files"); + break; + case -EFBIG: + jniThrowException(env, "java/lang/RuntimeException", + "File too large"); + break; + case -ENOSPC: + jniThrowException(env, "java/lang/RuntimeException", + "No space left on device"); + break; + case -ESPIPE: + jniThrowException(env, "java/lang/RuntimeException", + "Illegal seek"); + break; + case -EROFS: + jniThrowException(env, "java/lang/RuntimeException", + "Read-only file system"); + break; + case -EMLINK: + jniThrowException(env, "java/lang/RuntimeException", + "Too many links"); + break; default: ALOGE("Unknown binder error code. 0x%" PRIx32, err); String8 msg; diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index fae6698..27d3f39 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -51,6 +51,12 @@ #include <RenderNode.h> #include <CanvasProperty.h> +#ifdef USE_MINIKIN +#include <minikin/Layout.h> +#include "MinikinSkia.h" +#include "MinikinUtils.h" +#endif + #include <TextLayout.h> #include <TextLayoutCache.h> @@ -661,8 +667,58 @@ static float xOffsetForTextAlign(SkPaint* paint, float totalAdvance) { return 0; } +#ifdef USE_MINIKIN +static void renderTextLayout(OpenGLRenderer* renderer, Layout* layout, + jfloat x, jfloat y, SkPaint* paint) { + size_t nGlyphs = layout->nGlyphs(); + float* pos = new float[nGlyphs * 2]; + uint16_t* glyphs = new uint16_t[nGlyphs]; + SkTypeface* lastFace = 0; + SkTypeface* skFace = 0; + size_t start = 0; + MinikinRect b; + layout->getBounds(&b); + android::uirenderer::Rect bounds(b.mLeft, b.mTop, b.mRight, b.mBottom); + bounds.translate(x, y); + float totalAdvance = layout->getAdvance(); + + for (size_t i = 0; i < nGlyphs; i++) { + MinikinFontSkia* mfs = static_cast<MinikinFontSkia *>(layout->getFont(i)); + skFace = mfs->GetSkTypeface(); + glyphs[i] = layout->getGlyphId(i); + pos[2 * i] = layout->getX(i); + pos[2 * i + 1] = layout->getY(i); + if (i > 0 && skFace != lastFace) { + paint->setTypeface(lastFace); + size_t glyphsCount = i - start; + int bytesCount = glyphsCount * sizeof(jchar); + renderer->drawText((const char*) (glyphs + start), bytesCount, glyphsCount, + x, y, pos + 2 * start, paint, totalAdvance, bounds); + start = i; + } + lastFace = skFace; + } + if (skFace != NULL) { + paint->setTypeface(skFace); + size_t glyphsCount = nGlyphs - start; + int bytesCount = glyphsCount * sizeof(jchar); + renderer->drawText((const char*) (glyphs + start), bytesCount, glyphsCount, + x, y, pos + 2 * start, paint, totalAdvance, bounds); + } + delete[] glyphs; + delete[] pos; +} +#endif + static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, - jfloat x, jfloat y, int flags, SkPaint* paint) { + jfloat x, jfloat y, int flags, SkPaint* paint, TypefaceImpl* typeface) { +#ifdef USE_MINIKIN + Layout layout; + MinikinUtils::SetLayoutProperties(&layout, paint, flags, typeface); + layout.doLayout(text, count); + x += xOffsetForTextAlign(paint, layout.getAdvance()); + renderTextLayout(renderer, &layout, x, y, paint); +#else sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, text, 0, count, count, flags); if (value == NULL) { @@ -680,6 +736,7 @@ static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y, positions, paint, totalAdvance, bounds); +#endif } static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count, @@ -698,7 +755,14 @@ static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int co static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, jint start, jint count, jint contextCount, jfloat x, jfloat y, - int flags, SkPaint* paint) { + int flags, SkPaint* paint, TypefaceImpl* typeface) { +#ifdef USE_MINIKIN + Layout layout; + MinikinUtils::SetLayoutProperties(&layout, paint, flags, typeface); + layout.doLayout(text + start, count); + x += xOffsetForTextAlign(paint, layout.getAdvance()); + renderTextLayout(renderer, &layout, x, y, paint); +#else sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, text, start, count, contextCount, flags); if (value == NULL) { @@ -716,27 +780,30 @@ static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y, positions, paint, totalAdvance, bounds); +#endif } static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz, jlong rendererPtr, jcharArray text, jint index, jint count, - jfloat x, jfloat y, jint flags, jlong paintPtr) { + jfloat x, jfloat y, jint flags, jlong paintPtr, jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); jchar* textArray = env->GetCharArrayElements(text, NULL); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); - renderText(renderer, textArray + index, count, x, y, flags, paint); + renderText(renderer, textArray + index, count, x, y, flags, paint, typeface); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject clazz, jlong rendererPtr, jstring text, jint start, jint end, - jfloat x, jfloat y, jint flags, jlong paintPtr) { + jfloat x, jfloat y, jint flags, jlong paintPtr, jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); const jchar* textArray = env->GetStringChars(text, NULL); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); - renderText(renderer, textArray + start, end - start, x, y, flags, paint); + renderText(renderer, textArray + start, end - start, x, y, flags, paint, typeface); env->ReleaseStringChars(text, textArray); } @@ -769,28 +836,30 @@ static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz, static void android_view_GLES20Canvas_drawTextRunArray(JNIEnv* env, jobject clazz, jlong rendererPtr, jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y, jint dirFlags, - jlong paintPtr) { + jlong paintPtr, jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); jchar* textArray = env->GetCharArrayElements(text, NULL); SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); renderTextRun(renderer, textArray + contextIndex, index - contextIndex, - count, contextCount, x, y, dirFlags, paint); + count, contextCount, x, y, dirFlags, paint, typeface); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject clazz, jlong rendererPtr, jstring text, jint start, jint end, jint contextStart, int contextEnd, jfloat x, jfloat y, jint dirFlags, - jlong paintPtr) { + jlong paintPtr, jlong typefacePtr) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); const jchar* textArray = env->GetStringChars(text, NULL); jint count = end - start; jint contextCount = contextEnd - contextStart; SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr); renderTextRun(renderer, textArray + contextStart, start - contextStart, - count, contextCount, x, y, dirFlags, paint); + count, contextCount, x, y, dirFlags, paint, typeface); env->ReleaseStringChars(text, textArray); } @@ -1028,16 +1097,16 @@ static JNINativeMethod gMethods[] = { { "nSetupPaintFilter", "(JII)V", (void*) android_view_GLES20Canvas_setupPaintFilter }, { "nResetPaintFilter", "(J)V", (void*) android_view_GLES20Canvas_resetPaintFilter }, - { "nDrawText", "(J[CIIFFIJ)V", (void*) android_view_GLES20Canvas_drawTextArray }, - { "nDrawText", "(JLjava/lang/String;IIFFIJ)V", + { "nDrawText", "(J[CIIFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextArray }, + { "nDrawText", "(JLjava/lang/String;IIFFIJJ)V", (void*) android_view_GLES20Canvas_drawText }, { "nDrawTextOnPath", "(J[CIIJFFIJ)V", (void*) android_view_GLES20Canvas_drawTextArrayOnPath }, { "nDrawTextOnPath", "(JLjava/lang/String;IIJFFIJ)V", (void*) android_view_GLES20Canvas_drawTextOnPath }, - { "nDrawTextRun", "(J[CIIIIFFIJ)V", (void*) android_view_GLES20Canvas_drawTextRunArray }, - { "nDrawTextRun", "(JLjava/lang/String;IIIIFFIJ)V", + { "nDrawTextRun", "(J[CIIIIFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextRunArray }, + { "nDrawTextRun", "(JLjava/lang/String;IIIIFFIJJ)V", (void*) android_view_GLES20Canvas_drawTextRun }, { "nDrawPosText", "(J[CII[FJ)V", (void*) android_view_GLES20Canvas_drawPosTextArray }, diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 4715c26..867c1b1 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -80,6 +80,7 @@ static void android_view_RenderNode_setCaching(JNIEnv* env, jobject clazz, jlong renderNodePtr, jboolean caching) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setCaching(caching); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setStaticMatrix(JNIEnv* env, @@ -87,6 +88,7 @@ static void android_view_RenderNode_setStaticMatrix(JNIEnv* env, RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); renderNode->mutateStagingProperties().setStaticMatrix(matrix); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setAnimationMatrix(JNIEnv* env, @@ -94,24 +96,28 @@ static void android_view_RenderNode_setAnimationMatrix(JNIEnv* env, RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr); renderNode->mutateStagingProperties().setAnimationMatrix(matrix); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setClipToBounds(JNIEnv* env, jobject clazz, jlong renderNodePtr, jboolean clipToBounds) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setClipToBounds(clipToBounds); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setProjectBackwards(JNIEnv* env, jobject clazz, jlong renderNodePtr, jboolean shouldProject) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setProjectBackwards(shouldProject); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setProjectionReceiver(JNIEnv* env, jobject clazz, jlong renderNodePtr, jboolean shouldRecieve) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setProjectionReceiver(shouldRecieve); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setOutlineRoundRect(JNIEnv* env, @@ -119,7 +125,7 @@ static void android_view_RenderNode_setOutlineRoundRect(JNIEnv* env, jint right, jint bottom, jfloat radius) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom, radius); - renderNode->mutateStagingProperties().updateClipPath(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setOutlineConvexPath(JNIEnv* env, @@ -127,21 +133,21 @@ static void android_view_RenderNode_setOutlineConvexPath(JNIEnv* env, RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr); renderNode->mutateStagingProperties().mutableOutline().setConvexPath(outlinePath); - renderNode->mutateStagingProperties().updateClipPath(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setOutlineEmpty(JNIEnv* env, jobject clazz, jlong renderNodePtr) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().mutableOutline().setEmpty(); - renderNode->mutateStagingProperties().updateClipPath(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setClipToOutline(JNIEnv* env, jobject clazz, jlong renderNodePtr, jboolean clipToOutline) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().mutableOutline().setShouldClip(clipToOutline); - renderNode->mutateStagingProperties().updateClipPath(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setRevealClip(JNIEnv* env, @@ -150,115 +156,133 @@ static void android_view_RenderNode_setRevealClip(JNIEnv* env, RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().mutableRevealClip().set( shouldClip, inverseClip, x, y, radius); - renderNode->mutateStagingProperties().updateClipPath(); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setAlpha(JNIEnv* env, jobject clazz, jlong renderNodePtr, float alpha) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setAlpha(alpha); + renderNode->setPropertyFieldsDirty(RenderNode::ALPHA); } static void android_view_RenderNode_setHasOverlappingRendering(JNIEnv* env, jobject clazz, jlong renderNodePtr, bool hasOverlappingRendering) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setHasOverlappingRendering(hasOverlappingRendering); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setElevation(JNIEnv* env, jobject clazz, jlong renderNodePtr, float elevation) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setElevation(elevation); + renderNode->setPropertyFieldsDirty(RenderNode::Z); } static void android_view_RenderNode_setTranslationX(JNIEnv* env, jobject clazz, jlong renderNodePtr, float tx) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setTranslationX(tx); + renderNode->setPropertyFieldsDirty(RenderNode::TRANSLATION_X | RenderNode::X); } static void android_view_RenderNode_setTranslationY(JNIEnv* env, jobject clazz, jlong renderNodePtr, float ty) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setTranslationY(ty); + renderNode->setPropertyFieldsDirty(RenderNode::TRANSLATION_Y | RenderNode::Y); } static void android_view_RenderNode_setTranslationZ(JNIEnv* env, jobject clazz, jlong renderNodePtr, float tz) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setTranslationZ(tz); + renderNode->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z | RenderNode::Z); } static void android_view_RenderNode_setRotation(JNIEnv* env, jobject clazz, jlong renderNodePtr, float rotation) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setRotation(rotation); + renderNode->setPropertyFieldsDirty(RenderNode::ROTATION); } static void android_view_RenderNode_setRotationX(JNIEnv* env, jobject clazz, jlong renderNodePtr, float rx) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setRotationX(rx); + renderNode->setPropertyFieldsDirty(RenderNode::ROTATION_X); } static void android_view_RenderNode_setRotationY(JNIEnv* env, jobject clazz, jlong renderNodePtr, float ry) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setRotationY(ry); + renderNode->setPropertyFieldsDirty(RenderNode::ROTATION_Y); } static void android_view_RenderNode_setScaleX(JNIEnv* env, jobject clazz, jlong renderNodePtr, float sx) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setScaleX(sx); + renderNode->setPropertyFieldsDirty(RenderNode::SCALE_X); } static void android_view_RenderNode_setScaleY(JNIEnv* env, jobject clazz, jlong renderNodePtr, float sy) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setScaleY(sy); + renderNode->setPropertyFieldsDirty(RenderNode::SCALE_Y); } static void android_view_RenderNode_setPivotX(JNIEnv* env, jobject clazz, jlong renderNodePtr, float px) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setPivotX(px); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setPivotY(JNIEnv* env, jobject clazz, jlong renderNodePtr, float py) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setPivotY(py); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setCameraDistance(JNIEnv* env, jobject clazz, jlong renderNodePtr, float distance) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setCameraDistance(distance); + renderNode->setPropertyFieldsDirty(RenderNode::GENERIC); } static void android_view_RenderNode_setLeft(JNIEnv* env, jobject clazz, jlong renderNodePtr, int left) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setLeft(left); + renderNode->setPropertyFieldsDirty(RenderNode::X); } static void android_view_RenderNode_setTop(JNIEnv* env, jobject clazz, jlong renderNodePtr, int top) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setTop(top); + renderNode->setPropertyFieldsDirty(RenderNode::Y); } static void android_view_RenderNode_setRight(JNIEnv* env, jobject clazz, jlong renderNodePtr, int right) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setRight(right); + renderNode->setPropertyFieldsDirty(RenderNode::X); } static void android_view_RenderNode_setBottom(JNIEnv* env, jobject clazz, jlong renderNodePtr, int bottom) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setBottom(bottom); + renderNode->setPropertyFieldsDirty(RenderNode::Y); } static void android_view_RenderNode_setLeftTopRightBottom(JNIEnv* env, @@ -266,18 +290,21 @@ static void android_view_RenderNode_setLeftTopRightBottom(JNIEnv* env, int right, int bottom) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom); + renderNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); } static void android_view_RenderNode_offsetLeftAndRight(JNIEnv* env, jobject clazz, jlong renderNodePtr, float offset) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().offsetLeftRight(offset); + renderNode->setPropertyFieldsDirty(RenderNode::X); } static void android_view_RenderNode_offsetTopAndBottom(JNIEnv* env, jobject clazz, jlong renderNodePtr, float offset) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); renderNode->mutateStagingProperties().offsetTopBottom(offset); + renderNode->setPropertyFieldsDirty(RenderNode::Y); } // ---------------------------------------------------------------------------- @@ -290,6 +317,12 @@ static jboolean android_view_RenderNode_hasOverlappingRendering(JNIEnv* env, return renderNode->stagingProperties().hasOverlappingRendering(); } +static jboolean android_view_RenderNode_getClipToOutline(JNIEnv* env, + jobject clazz, jlong renderNodePtr) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + return renderNode->stagingProperties().getOutline().getShouldClip(); +} + static jfloat android_view_RenderNode_getAlpha(JNIEnv* env, jobject clazz, jlong renderNodePtr) { RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); @@ -510,6 +543,7 @@ static JNINativeMethod gMethods[] = { { "nOffsetTopAndBottom", "(JF)V", (void*) android_view_RenderNode_offsetTopAndBottom }, { "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering }, + { "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline }, { "nGetAlpha", "(J)F", (void*) android_view_RenderNode_getAlpha }, { "nGetLeft", "(J)F", (void*) android_view_RenderNode_getLeft }, { "nGetTop", "(J)F", (void*) android_view_RenderNode_getTop }, diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp index 5733f60..ea2f96e 100644 --- a/core/jni/android_view_RenderNodeAnimator.cpp +++ b/core/jni/android_view_RenderNodeAnimator.cpp @@ -62,7 +62,7 @@ public: mWeakThis = NULL; } - virtual void onAnimationFinished(BaseAnimator*) { + virtual void onAnimationFinished(BaseRenderNodeAnimator*) { JNIEnv* env = getEnv(mJvm); env->CallStaticVoidMethod( gRenderNodeAnimatorClassInfo.clazz, @@ -81,13 +81,6 @@ static inline RenderPropertyAnimator::RenderProperty toRenderProperty(jint prope return static_cast<RenderPropertyAnimator::RenderProperty>(property); } -static inline RenderPropertyAnimator::DeltaValueType toDeltaType(jint deltaType) { - LOG_ALWAYS_FATAL_IF(deltaType != RenderPropertyAnimator::DELTA - && deltaType != RenderPropertyAnimator::ABSOLUTE, - "Invalid delta type %d", deltaType); - return static_cast<RenderPropertyAnimator::DeltaValueType>(deltaType); -} - static inline CanvasPropertyPaintAnimator::PaintField toPaintField(jint field) { LOG_ALWAYS_FATAL_IF(field < 0 || field > CanvasPropertyPaintAnimator::ALPHA, @@ -96,49 +89,46 @@ static inline CanvasPropertyPaintAnimator::PaintField toPaintField(jint field) { } static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis, - jint propertyRaw, jint deltaTypeRaw, jfloat deltaValue) { + jint propertyRaw, jfloat finalValue) { RenderPropertyAnimator::RenderProperty property = toRenderProperty(propertyRaw); - RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw); - BaseAnimator* animator = new RenderPropertyAnimator(property, deltaType, deltaValue); + BaseRenderNodeAnimator* animator = new RenderPropertyAnimator(property, finalValue); animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz, - jobject weakThis, jlong canvasPropertyPtr, jint deltaTypeRaw, jfloat deltaValue) { - RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw); + jobject weakThis, jlong canvasPropertyPtr, jfloat finalValue) { CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr); - BaseAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, deltaType, deltaValue); + BaseRenderNodeAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, finalValue); animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz, jobject weakThis, jlong canvasPropertyPtr, jint paintFieldRaw, - jint deltaTypeRaw, jfloat deltaValue) { - RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw); + jfloat finalValue) { CanvasPropertyPaint* canvasProperty = reinterpret_cast<CanvasPropertyPaint*>(canvasPropertyPtr); CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw); - BaseAnimator* animator = new CanvasPropertyPaintAnimator( - canvasProperty, paintField, deltaType, deltaValue); + BaseRenderNodeAnimator* animator = new CanvasPropertyPaintAnimator( + canvasProperty, paintField, finalValue); animator->setListener(new AnimationListenerBridge(env, weakThis)); return reinterpret_cast<jlong>( animator ); } static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint duration) { LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative"); - BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr); + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); animator->setDuration(duration); } static jint getDuration(JNIEnv* env, jobject clazz, jlong animatorPtr) { - BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr); + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); return static_cast<jint>(animator->duration()); } static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong interpolatorPtr) { - BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr); + BaseRenderNodeAnimator* animator = reinterpret_cast<BaseRenderNodeAnimator*>(animatorPtr); Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr); animator->setInterpolator(interpolator); } @@ -153,9 +143,9 @@ const char* const kClassPathName = "android/view/RenderNodeAnimator"; static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER - { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IIF)J", (void*) createAnimator }, - { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator }, - { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator }, + { "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IF)J", (void*) createAnimator }, + { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JF)J", (void*) createCanvasPropertyFloatAnimator }, + { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyPaintAnimator }, { "nSetDuration", "(JI)V", (void*) setDuration }, { "nGetDuration", "(J)I", (void*) getDuration }, { "nSetInterpolator", "(JJ)V", (void*) setInterpolator }, diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index d130a6d..2c10212 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -92,9 +92,9 @@ private: class OnFinishedEvent { public: - OnFinishedEvent(BaseAnimator* animator, AnimationListener* listener) + OnFinishedEvent(BaseRenderNodeAnimator* animator, AnimationListener* listener) : animator(animator), listener(listener) {} - sp<BaseAnimator> animator; + sp<BaseRenderNodeAnimator> animator; sp<AnimationListener> listener; }; @@ -127,7 +127,7 @@ public: virtual ~RootRenderNode() {} - virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) { + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) { OnFinishedEvent event(animator, listener); mOnFinishedEvents.push_back(event); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index cdb77f1..4eac802 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2613,7 +2613,7 @@ <!-- Must be required by an {@link android.service.trust.TrustAgentService}, to ensure that only the system can bind to it. --> - <permission android:name="android.permission.BIND_TRUST_AGENT_SERVICE" + <permission android:name="android.permission.BIND_TRUST_AGENT" android:protectionLevel="signature" android:label="@string/permlab_bind_trust_agent_service" android:description="@string/permdesc_bind_trust_agent_service" /> diff --git a/core/res/res/layout/notification_action.xml b/core/res/res/layout/notification_action.xml deleted file mode 100644 index 4e7c74c..0000000 --- a/core/res/res/layout/notification_action.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 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. ---> - -<Button xmlns:android="http://schemas.android.com/apk/res/android" - style="?android:attr/borderlessButtonStyle" - android:id="@+id/action0" - android:layout_width="0dp" - android:layout_height="48dp" - android:layout_weight="1" - android:gravity="start|center_vertical" - android:drawablePadding="8dp" - android:paddingStart="8dp" - android:textColor="#ccc" - android:textSize="14dp" - android:singleLine="true" - android:ellipsize="end" - /> diff --git a/core/res/res/layout/notification_action_tombstone.xml b/core/res/res/layout/notification_action_tombstone.xml deleted file mode 100644 index 9977cfe..0000000 --- a/core/res/res/layout/notification_action_tombstone.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 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. ---> - -<Button xmlns:android="http://schemas.android.com/apk/res/android" - style="?android:attr/borderlessButtonStyle" - android:id="@+id/action0" - android:layout_width="0dp" - android:layout_height="48dp" - android:layout_weight="1" - android:gravity="start|center_vertical" - android:drawablePadding="8dp" - android:paddingStart="8dp" - android:textColor="#ccc" - android:textSize="14dp" - android:singleLine="true" - android:ellipsize="end" - android:alpha="0.5" - android:enabled="false" - /> diff --git a/core/res/res/layout/notification_template_base.xml b/core/res/res/layout/notification_template_base.xml deleted file mode 100644 index d2e25c1..0000000 --- a/core/res/res/layout/notification_template_base.xml +++ /dev/null @@ -1,136 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 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. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:background="@android:drawable/notification_bg" - android:id="@+id/status_bar_latest_event_content" - android:layout_width="match_parent" - android:layout_height="64dp" - internal:layout_minHeight="64dp" - internal:layout_maxHeight="64dp" - > - <ImageView android:id="@+id/icon" - android:layout_width="@dimen/notification_large_icon_width" - android:layout_height="@dimen/notification_large_icon_height" - android:background="@android:drawable/notification_template_icon_bg" - android:scaleType="center" - /> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="fill_vertical" - android:layout_marginStart="@dimen/notification_large_icon_width" - android:minHeight="@dimen/notification_large_icon_height" - android:orientation="vertical" - android:paddingEnd="8dp" - android:paddingTop="2dp" - android:paddingBottom="2dp" - android:gravity="top" - > - <LinearLayout - android:id="@+id/line1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="6dp" - android:layout_marginStart="8dp" - android:orientation="horizontal" - > - <TextView android:id="@+id/title" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:layout_weight="1" - /> - <ViewStub android:id="@+id/time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="0" - android:visibility="gone" - android:layout="@layout/notification_template_part_time" - /> - <ViewStub android:id="@+id/chronometer" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="0" - android:visibility="gone" - android:layout="@layout/notification_template_part_chronometer" - /> - </LinearLayout> - <TextView android:id="@+id/text2" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Line2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="-2dp" - android:layout_marginBottom="-2dp" - android:layout_marginStart="8dp" - android:singleLine="true" - android:fadingEdge="horizontal" - android:ellipsize="marquee" - android:visibility="gone" - /> - <ProgressBar - android:id="@android:id/progress" - android:layout_width="match_parent" - android:layout_height="12dp" - android:layout_marginStart="8dp" - android:visibility="gone" - style="?android:attr/progressBarStyleHorizontal" - /> - <LinearLayout - android:id="@+id/line3" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:gravity="center_vertical" - android:layout_marginStart="8dp" - > - <TextView android:id="@+id/text" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - /> - <TextView android:id="@+id/info" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_weight="0" - android:singleLine="true" - android:gravity="center" - android:paddingStart="8dp" - /> - <ImageView android:id="@+id/right_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginStart="8dp" - android:scaleType="centerInside" - android:visibility="gone" - android:drawableAlpha="153" - /> - </LinearLayout> - </LinearLayout> -</FrameLayout> diff --git a/core/res/res/layout/notification_template_big_base.xml b/core/res/res/layout/notification_template_big_base.xml deleted file mode 100644 index 7cc6650..0000000 --- a/core/res/res/layout/notification_template_big_base.xml +++ /dev/null @@ -1,167 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 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. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:background="@android:drawable/notification_bg" - android:id="@+id/status_bar_latest_event_content" - android:layout_width="match_parent" - android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded" - > - <ImageView android:id="@+id/icon" - android:layout_width="@dimen/notification_large_icon_width" - android:layout_height="@dimen/notification_large_icon_height" - android:background="@android:drawable/notification_template_icon_bg" - android:scaleType="center" - /> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="fill_vertical" - android:minHeight="@dimen/notification_large_icon_height" - android:orientation="vertical" - android:gravity="top" - > - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/notification_large_icon_width" - android:minHeight="@dimen/notification_large_icon_height" - android:paddingTop="2dp" - android:orientation="vertical" - > - <LinearLayout - android:id="@+id/line1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="6dp" - android:layout_marginEnd="8dp" - android:layout_marginStart="8dp" - android:orientation="horizontal" - > - <TextView android:id="@+id/title" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:layout_weight="1" - /> - <ViewStub android:id="@+id/time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="0" - android:visibility="gone" - android:layout="@layout/notification_template_part_time" - /> - <ViewStub android:id="@+id/chronometer" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="0" - android:visibility="gone" - android:layout="@layout/notification_template_part_chronometer" - /> - </LinearLayout> - <TextView android:id="@+id/text2" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Line2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="-2dp" - android:layout_marginBottom="-2dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:fadingEdge="horizontal" - android:ellipsize="marquee" - android:visibility="gone" - /> - <TextView android:id="@+id/big_text" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="false" - android:visibility="gone" - /> - <LinearLayout - android:id="@+id/line3" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:orientation="horizontal" - android:gravity="center_vertical" - > - <TextView android:id="@+id/text" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - /> - <TextView android:id="@+id/info" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_weight="0" - android:singleLine="true" - android:gravity="center" - android:paddingStart="8dp" - /> - <ImageView android:id="@+id/right_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginStart="8dp" - android:scaleType="centerInside" - android:visibility="gone" - android:drawableAlpha="153" - /> - </LinearLayout> - <ProgressBar - android:id="@android:id/progress" - android:layout_width="match_parent" - android:layout_height="12dp" - android:layout_marginBottom="8dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:visibility="gone" - style="?android:attr/progressBarStyleHorizontal" - /> - </LinearLayout> - <ImageView - android:layout_width="match_parent" - android:layout_height="1px" - android:id="@+id/action_divider" - android:visibility="gone" - android:background="?android:attr/dividerHorizontal" /> - <include - layout="@layout/notification_action_list" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/notification_large_icon_width" - /> - </LinearLayout> -</FrameLayout> diff --git a/core/res/res/layout/notification_template_big_picture.xml b/core/res/res/layout/notification_template_big_picture.xml deleted file mode 100644 index f3f3951..0000000 --- a/core/res/res/layout/notification_template_big_picture.xml +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 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. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:background="@android:drawable/notification_bg" - android:id="@+id/status_bar_latest_event_content" - android:layout_width="match_parent" - android:layout_height="match_parent" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded" - > - <ImageView - android:id="@+id/big_picture" - android:layout_width="match_parent" - android:layout_height="192dp" - android:layout_marginTop="64dp" - android:layout_gravity="bottom" - android:scaleType="centerCrop" - /> - <ImageView - android:layout_width="match_parent" - android:layout_height="6dp" - android:layout_marginTop="64dp" - android:scaleType="fitXY" - android:src="@drawable/title_bar_shadow" - /> - <include layout="@layout/notification_template_base" - android:layout_width="match_parent" - android:layout_height="wrap_content" - /> - <FrameLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="208dp" - android:paddingStart="64dp" - android:layout_gravity="bottom" - android:background="#CC111111" - > - <include - layout="@layout/notification_action_list" - android:id="@+id/actions" - android:layout_gravity="bottom" - android:layout_width="match_parent" - android:layout_height="wrap_content" - /> - </FrameLayout> -</FrameLayout> diff --git a/core/res/res/layout/notification_template_big_text.xml b/core/res/res/layout/notification_template_big_text.xml deleted file mode 100644 index 7e6da22..0000000 --- a/core/res/res/layout/notification_template_big_text.xml +++ /dev/null @@ -1,183 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 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. ---> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:background="@android:drawable/notification_bg" - android:id="@+id/status_bar_latest_event_content" - android:layout_width="match_parent" - android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded" - > - <ImageView android:id="@+id/icon" - android:layout_width="@dimen/notification_large_icon_width" - android:layout_height="@dimen/notification_large_icon_height" - android:background="@android:drawable/notification_template_icon_bg" - android:scaleType="center" - /> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="fill_vertical" - android:layout_marginStart="@dimen/notification_large_icon_width" - android:orientation="vertical" - android:paddingTop="0dp" - android:paddingBottom="2dp" - android:gravity="top" - > - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="@dimen/notification_large_icon_height" - android:orientation="vertical" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:layout_weight="1" - > - <LinearLayout - android:id="@+id/line1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="8dp" - android:orientation="horizontal" - android:layout_gravity="top" - android:layout_weight="0" - > - <TextView android:id="@+id/title" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:layout_weight="1" - /> - <ViewStub android:id="@+id/time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="0" - android:visibility="gone" - android:layout="@layout/notification_template_part_time" - /> - <ViewStub android:id="@+id/chronometer" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="0" - android:visibility="gone" - android:layout="@layout/notification_template_part_chronometer" - /> - </LinearLayout> - <TextView android:id="@+id/text2" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Line2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="-2dp" - android:layout_marginBottom="-2dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:fadingEdge="horizontal" - android:ellipsize="marquee" - android:layout_weight="0" - android:visibility="gone" - /> - <ProgressBar - android:id="@android:id/progress" - android:layout_width="match_parent" - android:layout_height="12dp" - android:layout_marginBottom="8dp" - android:layout_marginEnd="8dp" - android:visibility="gone" - android:layout_weight="0" - style="?android:attr/progressBarStyleHorizontal" - /> - <TextView android:id="@+id/big_text" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginBottom="10dp" - android:layout_marginEnd="8dp" - android:singleLine="false" - android:visibility="gone" - android:maxLines="8" - android:ellipsize="end" - android:layout_weight="1" - /> - </LinearLayout> - <ImageView - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="-1px" - android:id="@+id/action_divider" - android:visibility="gone" - android:background="?android:attr/dividerHorizontal" /> - <include - layout="@layout/notification_action_list" - android:layout_width="match_parent" - android:layout_height="0dp" - android:visibility="gone" - android:layout_weight="1" - /> - <ImageView - android:layout_width="match_parent" - android:layout_height="1px" - android:id="@+id/overflow_divider" - android:layout_marginBottom="8dp" - android:visibility="visible" - android:background="?android:attr/dividerHorizontal" /> - <LinearLayout - android:id="@+id/line3" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginBottom="8dp" - android:layout_marginEnd="8dp" - android:orientation="horizontal" - android:layout_weight="0" - android:gravity="center_vertical" - > - <TextView android:id="@+id/text" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - /> - <TextView android:id="@+id/info" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_weight="0" - android:singleLine="true" - android:gravity="center" - android:paddingStart="8dp" - /> - <ImageView android:id="@+id/right_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginStart="8dp" - android:scaleType="centerInside" - android:visibility="gone" - android:drawableAlpha="153" - /> - </LinearLayout> - </LinearLayout> -</FrameLayout> diff --git a/core/res/res/layout/notification_template_icon_group.xml b/core/res/res/layout/notification_template_icon_group.xml new file mode 100644 index 0000000..2ad6f9e --- /dev/null +++ b/core/res/res/layout/notification_template_icon_group.xml @@ -0,0 +1,42 @@ +<?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 + --> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:internal="http://schemas.android.com/apk/prv/res/android" + android:layout_width="@dimen/notification_large_icon_width" + android:layout_height="@dimen/notification_large_icon_height" + android:id="@+id/icon_group" + > + <ImageView android:id="@+id/icon" + android:layout_width="@dimen/notification_large_icon_width" + android:layout_height="@dimen/notification_large_icon_height" + android:padding="8dp" + android:scaleType="centerInside" + /> + <ImageView android:id="@+id/right_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:padding="4dp" + android:layout_gravity="end|bottom" + android:scaleType="centerInside" + android:visibility="gone" + android:layout_marginEnd="3dp" + android:layout_marginBottom="3dp" + /> +</FrameLayout> + diff --git a/core/res/res/layout/notification_template_inbox.xml b/core/res/res/layout/notification_template_inbox.xml deleted file mode 100644 index 1eec871..0000000 --- a/core/res/res/layout/notification_template_inbox.xml +++ /dev/null @@ -1,267 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 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. ---> - -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:internal="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/status_bar_latest_event_content" - android:background="@android:drawable/notification_bg" - android:layout_width="match_parent" - android:layout_height="wrap_content" - internal:layout_minHeight="65dp" - internal:layout_maxHeight="unbounded" - > - <ImageView android:id="@+id/icon" - android:layout_width="@dimen/notification_large_icon_width" - android:layout_height="@dimen/notification_large_icon_height" - android:background="@android:drawable/notification_template_icon_bg" - android:scaleType="center" - /> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="fill_vertical" - android:layout_marginStart="@dimen/notification_large_icon_width" - android:minHeight="@dimen/notification_large_icon_height" - android:orientation="vertical" - android:paddingTop="0dp" - android:paddingBottom="2dp" - android:gravity="top" - > - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="@dimen/notification_large_icon_height" - android:paddingTop="2dp" - android:orientation="vertical" - > - <LinearLayout - android:id="@+id/line1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:paddingTop="6dp" - android:orientation="horizontal" - android:layout_weight="0" - > - <TextView android:id="@+id/title" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - android:layout_weight="1" - /> - <ViewStub android:id="@+id/time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="0" - android:visibility="gone" - android:layout="@layout/notification_template_part_time" - /> - <ViewStub android:id="@+id/chronometer" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="0" - android:visibility="gone" - android:layout="@layout/notification_template_part_chronometer" - /> - </LinearLayout> - <TextView android:id="@+id/text2" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Line2" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="-2dp" - android:layout_marginBottom="-2dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:fadingEdge="horizontal" - android:ellipsize="marquee" - android:visibility="gone" - android:layout_weight="0" - /> - <ProgressBar - android:id="@android:id/progress" - android:layout_width="match_parent" - android:layout_height="12dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:visibility="gone" - android:layout_weight="0" - style="?android:attr/progressBarStyleHorizontal" - /> - <TextView android:id="@+id/inbox_text0" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:ellipsize="end" - android:visibility="gone" - android:layout_weight="1" - /> - <TextView android:id="@+id/inbox_text1" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:ellipsize="end" - android:visibility="gone" - android:layout_weight="1" - /> - <TextView android:id="@+id/inbox_text2" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:ellipsize="end" - android:visibility="gone" - android:layout_weight="1" - /> - <TextView android:id="@+id/inbox_text3" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:ellipsize="end" - android:visibility="gone" - android:layout_weight="1" - /> - <TextView android:id="@+id/inbox_text4" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:singleLine="true" - android:ellipsize="end" - android:visibility="gone" - android:layout_weight="1" - /> - <TextView android:id="@+id/inbox_text5" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:ellipsize="end" - android:visibility="gone" - android:layout_weight="1" - /> - <TextView android:id="@+id/inbox_text6" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:ellipsize="end" - android:visibility="gone" - android:layout_weight="1" - /> - <TextView android:id="@+id/inbox_more" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:singleLine="true" - android:ellipsize="end" - android:visibility="gone" - android:layout_weight="1" - android:text="@android:string/ellipsis" - /> - <FrameLayout - android:id="@+id/inbox_end_pad" - android:layout_width="match_parent" - android:layout_height="8dip" - android:visibility="gone" - android:layout_weight="0" - /> - </LinearLayout> - <ImageView - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="-1px" - android:id="@+id/action_divider" - android:background="?android:attr/dividerHorizontal" /> - <include - layout="@layout/notification_action_list" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_weight="0" - /> - <ImageView - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_marginTop="-1px" - android:id="@+id/overflow_divider" - android:visibility="visible" - android:background="?android:attr/dividerHorizontal" /> - <LinearLayout - android:id="@+id/line3" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginStart="8dp" - android:layout_marginBottom="8dp" - android:layout_marginEnd="8dp" - android:orientation="horizontal" - android:layout_weight="0" - android:gravity="center_vertical" - > - <TextView android:id="@+id/text" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:layout_gravity="center" - android:singleLine="true" - android:ellipsize="marquee" - android:fadingEdge="horizontal" - /> - <TextView android:id="@+id/info" - android:textAppearance="@style/TextAppearance.StatusBar.EventContent.Info" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_weight="0" - android:singleLine="true" - android:gravity="center" - android:paddingStart="8dp" - /> - <ImageView android:id="@+id/right_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginStart="8dp" - android:scaleType="centerInside" - android:visibility="gone" - android:drawableAlpha="153" - /> - </LinearLayout> - </LinearLayout> -</FrameLayout> diff --git a/core/res/res/layout/notification_template_quantum_base.xml b/core/res/res/layout/notification_template_quantum_base.xml index 8f3019d..789bf32 100644 --- a/core/res/res/layout/notification_template_quantum_base.xml +++ b/core/res/res/layout/notification_template_quantum_base.xml @@ -23,10 +23,9 @@ internal:layout_minHeight="64dp" internal:layout_maxHeight="64dp" > - <ImageView android:id="@+id/icon" + <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" android:layout_height="@dimen/notification_large_icon_height" - android:scaleType="center" /> <LinearLayout android:layout_width="match_parent" @@ -91,7 +90,7 @@ android:layout_height="12dp" android:layout_marginStart="8dp" android:visibility="gone" - style="@style/Widget.Quantum.Light.ProgressBar.Horizontal" + style="@style/Widget.StatusBar.Quantum.ProgressBar" /> <LinearLayout android:id="@+id/line3" @@ -121,16 +120,6 @@ android:gravity="center" android:paddingStart="8dp" /> - <ImageView android:id="@+id/right_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginStart="8dp" - android:scaleType="centerInside" - android:visibility="gone" - android:drawableAlpha="153" - /> </LinearLayout> </LinearLayout> </FrameLayout> diff --git a/core/res/res/layout/notification_template_quantum_big_base.xml b/core/res/res/layout/notification_template_quantum_big_base.xml index 45e69b1..8cb5549 100644 --- a/core/res/res/layout/notification_template_quantum_big_base.xml +++ b/core/res/res/layout/notification_template_quantum_big_base.xml @@ -23,10 +23,9 @@ internal:layout_minHeight="65dp" internal:layout_maxHeight="unbounded" > - <ImageView android:id="@+id/icon" + <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" android:layout_height="@dimen/notification_large_icon_height" - android:scaleType="center" /> <LinearLayout android:layout_width="match_parent" @@ -128,16 +127,6 @@ android:gravity="center" android:paddingStart="8dp" /> - <ImageView android:id="@+id/right_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginStart="8dp" - android:scaleType="centerInside" - android:visibility="gone" - android:drawableAlpha="153" - /> </LinearLayout> <ProgressBar android:id="@android:id/progress" diff --git a/core/res/res/layout/notification_template_quantum_big_text.xml b/core/res/res/layout/notification_template_quantum_big_text.xml index f7769d7..bbd1071 100644 --- a/core/res/res/layout/notification_template_quantum_big_text.xml +++ b/core/res/res/layout/notification_template_quantum_big_text.xml @@ -22,10 +22,9 @@ internal:layout_minHeight="65dp" internal:layout_maxHeight="unbounded" > - <ImageView android:id="@+id/icon" + <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" android:layout_height="@dimen/notification_large_icon_height" - android:scaleType="center" /> <LinearLayout android:layout_width="match_parent" @@ -166,16 +165,6 @@ android:gravity="center" android:paddingStart="8dp" /> - <ImageView android:id="@+id/right_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginStart="8dp" - android:scaleType="centerInside" - android:visibility="gone" - android:drawableAlpha="153" - /> </LinearLayout> </LinearLayout> </FrameLayout> diff --git a/core/res/res/layout/notification_template_quantum_inbox.xml b/core/res/res/layout/notification_template_quantum_inbox.xml index 04974c4..a071d59 100644 --- a/core/res/res/layout/notification_template_quantum_inbox.xml +++ b/core/res/res/layout/notification_template_quantum_inbox.xml @@ -23,10 +23,9 @@ internal:layout_minHeight="65dp" internal:layout_maxHeight="unbounded" > - <ImageView android:id="@+id/icon" + <include layout="@layout/notification_template_icon_group" android:layout_width="@dimen/notification_large_icon_width" android:layout_height="@dimen/notification_large_icon_height" - android:scaleType="center" /> <LinearLayout android:layout_width="match_parent" @@ -250,16 +249,6 @@ android:gravity="center" android:paddingStart="8dp" /> - <ImageView android:id="@+id/right_icon" - android:layout_width="16dp" - android:layout_height="16dp" - android:layout_gravity="center" - android:layout_weight="0" - android:layout_marginStart="8dp" - android:scaleType="centerInside" - android:visibility="gone" - android:drawableAlpha="153" - /> </LinearLayout> </LinearLayout> </FrameLayout> diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml index b3db01a..dc78174 100644 --- a/core/res/res/layout/status_bar_latest_event_content.xml +++ b/core/res/res/layout/status_bar_latest_event_content.xml @@ -22,7 +22,7 @@ android:layout_height="wrap_content" android:background="#FFFF00FF" > - <include layout="@layout/notification_template_base" + <include layout="@layout/notification_template_quantum_base" android:layout_width="match_parent" android:layout_height="wrap_content" /> diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml index bee6c21..dbe2d7f 100644 --- a/core/res/res/values-km-rKH/strings.xml +++ b/core/res/res/values-km-rKH/strings.xml @@ -351,8 +351,6 @@ <string name="permdesc_broadcastSmsReceived" msgid="4152037720034365492">"ឲ្យកម្មវិធីប្រកាសការជូនដំណឹងការទទួលសារ SMS ។ កម្មវិធីព្យាបាទអាចប្រើវាដើម្បីបន្លំសារ SMS ចូល។"</string> <string name="permlab_broadcastWapPush" msgid="3145347413028582371">"ផ្ញើការប្រកាសបានទទួល WAP-PUSH"</string> <string name="permdesc_broadcastWapPush" msgid="4783402525039442729">"ឲ្យកម្មវិធីប្រកាសការជូនដំណឹងថាបានទទួលសារ WAP PUSH ។ កម្មវិធីព្យាបាទអាចប្រើវាដើម្បីក្លែងបង្កាន់ដៃសារ MMS ឬជំនួសមាតិកាទំព័របណ្ដាញណាមួយស្ងាត់ៗដោយអ្វីដែលក្លែងក្លាយ។"</string> - <string name="permlab_broadcastScoreNetworks" msgid="6432008366605475024">"ផ្ញើពិន្ទុការប្រកាសបណ្ដាញ"</string> - <string name="permdesc_broadcastScoreNetworks" msgid="7652980974435077828">"ឲ្យកម្មវិធីប្រកាសការជូនដំណឹងដែលបណ្ដាញតម្រូវឲ្យដាក់ពិន្ទុ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតា។"</string> <string name="permlab_setProcessLimit" msgid="2451873664363662666">"កំណត់ចំនួនដំណើរការដែលកំពុងដំណើរការ"</string> <string name="permdesc_setProcessLimit" msgid="7318061314040879542">"ឲ្យកម្មវិធីពិនិត្យចំនួនដំណើរការអតិបរមាដែលនឹងដំណើរការ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_setAlwaysFinish" msgid="550958507798796965">"បង្ខំឲ្យបិទកម្មវិធីក្នុងផ្ទៃខាងក្រោយ"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 4a12386..310de65 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -932,7 +932,7 @@ <string name="js_dialog_title" msgid="1987483977834603872">"На страници на адреси „<xliff:g id="TITLE">%s</xliff:g>“ пише следеће:"</string> <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string> <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"Потврда навигације"</string> - <string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"Напусти ову страницу"</string> + <string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"Затвори ову страницу"</string> <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Остани на овој страници"</string> <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nДа ли стварно желите да напустите ову страницу?"</string> <string name="save_password_label" msgid="6860261758665825069">"Потврда"</string> diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml index 44e258d..8d82a17 100644 --- a/core/res/res/values-watch/config.xml +++ b/core/res/res/values-watch/config.xml @@ -21,9 +21,8 @@ for watch products. Do not translate. --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Only show power and settings items due to smaller real estate. --> + <!-- Only show settings item due to smaller real estate. --> <string-array translatable="false" name="config_globalActionsList"> - <item>power</item> <item>settings</item> </string-array> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 327f6cf..1d35c84 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -657,7 +657,9 @@ <attr name="popupMenuStyle" format="reference" /> <!-- Default StackView style. --> <attr name="stackViewStyle" format="reference" /> - <!-- Default style for the FragmentBreadCrumbs widget. --> + + <!-- Default style for the FragmentBreadCrumbs widget. This widget is deprecated + starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). --> <attr name="fragmentBreadCrumbsStyle" format="reference" /> <!-- NumberPicker style. --> @@ -1775,6 +1777,35 @@ finishes. Corresponds to {@link android.view.Window#setAllowExitTransitionOverlap(boolean)}. --> <attr name="windowAllowExitTransitionOverlap"/> + + <!-- Flag indicating whether this Window is responsible for drawing the background for the + system bars. If true and the window is not floating, the system bars are drawn with a + transparent background and the corresponding areas in this window are filled with the + colors specified in {@link android.R.attr#statusBarColor} and + {@link android.R.attr#navigationBarColor}. Corresponds to + {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS}. --> + <attr name="windowDrawsSystemBarBackgrounds" format="boolean" /> + + <!-- The color for the status bar. If the color is not opaque, consider setting + {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and + {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}. + For this to take effect, the window must be drawing the system bar backgrounds with + {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the status bar must not + have been requested to be translucent with + {@link android.R.attr#windowTranslucentStatus}. + Corresponds to {@link android.view.Window#setStatusBarColor(int)}. --> + <attr name="statusBarColor" format="color" /> + + <!-- The color for the navigation bar. If the color is not opaque, consider setting + {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and + {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}. + For this to take effect, the window must be drawing the system bar backgrounds with + {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not + have been requested to be translucent with + {@link android.R.attr#windowTranslucentNavigation}. + Corresponds to {@link android.view.Window#setNavigationBarColor(int)}. --> + <attr name="navigationBarColor" format="color" /> + </declare-styleable> <!-- The set of attributes that describe a AlertDialog's theme. --> @@ -2477,7 +2508,7 @@ when doing an Activity transition. Typically, the elements inside a ViewGroup are each transitioned from the scene individually. The default for a ViewGroup is false unless it has a background. See - {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window, + {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity, android.view.View, String)} for more information. Corresponds to {@link android.view.ViewGroup#setTransitionGroup(boolean)}.--> <attr name="transitionGroup" format="boolean" /> @@ -4989,6 +5020,15 @@ <attr name="startDelay" format="integer" /> <!-- Interpolator to be used in the animations spawned by this transition. --> <attr name="interpolator" /> + <!-- The match order to use for the transition. This is a comma-separated + list of values, containing one or more of the following: + id, itemId, viewName, instance. These correspond to + {@link android.transition.Transition#MATCH_ID}, + {@link android.transition.Transition#MATCH_ITEM_ID}, + {@link android.transition.Transition#MATCH_VIEW_NAME}, and + {@link android.transition.Transition#MATCH_INSTANCE}, respectively. + This corresponds to {@link android.transition.Transition#setMatchOrder(int...)}. --> + <attr name="matchOrder" format="string" /> </declare-styleable> <!-- Use <code>fade</code>as the root tag of the XML resource that @@ -6075,13 +6115,13 @@ <attr name="settingsActivity" /> </declare-styleable> - <!-- Use <code>trust_agent</code> as the root tag of the XML resource that + <!-- Use <code>trust-agent</code> as the root tag of the XML resource that describes an {@link android.service.trust.TrustAgentService}, which is referenced from its {@link android.service.trust.TrustAgentService#TRUST_AGENT_META_DATA} meta-data entry. Described here are the attributes that can be included in that tag. --> <declare-styleable name="TrustAgent"> <!-- Component name of an activity that allows the user to modify - the settings for this TrustAgent. --> + the settings for this trust agent. --> <attr name="settingsActivity" /> </declare-styleable> @@ -6617,6 +6657,10 @@ <attr name="titleMarginEnd" format="dimension" /> <attr name="titleMarginTop" format="dimension" /> <attr name="titleMarginBottom" format="dimension" /> + <attr name="contentInsetStart" format="dimension" /> + <attr name="contentInsetEnd" format="dimension" /> + <attr name="contentInsetLeft" format="dimension" /> + <attr name="contentInsetRight" format="dimension" /> </declare-styleable> <declare-styleable name="Toolbar_LayoutParams"> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 69b11cd..52b021f 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -227,9 +227,9 @@ <dimen name="action_bar_stacked_tab_max_width">180dp</dimen> <!-- Size of notification text (see TextAppearance.StatusBar.EventContent) --> - <dimen name="notification_text_size">14dp</dimen> + <dimen name="notification_text_size">13dp</dimen> <!-- Size of notification text titles (see TextAppearance.StatusBar.EventContent.Title) --> - <dimen name="notification_title_text_size">18dp</dimen> + <dimen name="notification_title_text_size">16dp</dimen> <!-- Size of smaller notification text (see TextAppearance.StatusBar.EventContent.Line2, Info, Time) --> <dimen name="notification_subtext_size">12dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 3ef4ab6..44b25f2b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2104,7 +2104,6 @@ <public-padding type="attr" name="l_resource_pad" end="0x1010410" /> - <public type="attr" name="fragmentBreadCrumbsStyle" /> <public type="attr" name="fastScrollStyle" /> <public type="attr" name="windowContentTransitions" /> <public type="attr" name="windowContentTransitionManager" /> @@ -2170,6 +2169,14 @@ <public type="attr" name="splitTrack" /> <public type="attr" name="targetViewName" /> <public type="attr" name="excludeViewName" /> + <public type="attr" name="matchOrder" /> + <public type="attr" name="windowDrawsSystemBarBackgrounds" /> + <public type="attr" name="statusBarColor"/> + <public type="attr" name="navigationBarColor"/> + <public type="attr" name="contentInsetStart" /> + <public type="attr" name="contentInsetEnd" /> + <public type="attr" name="contentInsetLeft" /> + <public type="attr" name="contentInsetRight" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> @@ -2187,20 +2194,16 @@ <public type="style" name="Widget.StackView" /> <public type="style" name="Widget.Holo.FastScroll" /> - <public type="style" name="Widget.Holo.FragmentBreadCrumbs" /> <public type="style" name="Widget.Holo.StackView" /> <public type="style" name="Widget.Holo.Light.Button.Borderless" /> <public type="style" name="Widget.Holo.Light.FastScroll" /> - <public type="style" name="Widget.Holo.Light.FragmentBreadCrumbs" /> <public type="style" name="Widget.Holo.Light.StackView" /> <public type="style" name="Widget.DeviceDefault.FastScroll" /> - <public type="style" name="Widget.DeviceDefault.FragmentBreadCrumbs" /> <public type="style" name="Widget.DeviceDefault.StackView" /> <public type="style" name="Widget.DeviceDefault.Light.FastScroll" /> - <public type="style" name="Widget.DeviceDefault.Light.FragmentBreadCrumbs" /> <public type="style" name="Widget.DeviceDefault.Light.StackView" /> <public type="style" name="TextAppearance.Quantum" /> @@ -2269,6 +2272,12 @@ <public type="style" name="Theme.Quantum.Light.NoActionBar.TranslucentDecor" /> <public type="style" name="Theme.Quantum.Light.Panel" /> + <public type="style" name="ThemeOverlay" /> + <public type="style" name="ThemeOverlay.Quantum" /> + <public type="style" name="ThemeOverlay.Quantum.Light" /> + <public type="style" name="ThemeOverlay.Quantum.Dark" /> + <public type="style" name="ThemeOverlay.Quantum.ActionBarWidget" /> + <public type="style" name="Widget.Quantum" /> <public type="style" name="Widget.Quantum.ActionBar" /> <public type="style" name="Widget.Quantum.ActionBar.Solid" /> @@ -2299,7 +2308,6 @@ <public type="style" name="Widget.Quantum.EditText" /> <public type="style" name="Widget.Quantum.ExpandableListView" /> <public type="style" name="Widget.Quantum.FastScroll" /> - <public type="style" name="Widget.Quantum.FragmentBreadCrumbs" /> <public type="style" name="Widget.Quantum.GridView" /> <public type="style" name="Widget.Quantum.HorizontalScrollView" /> <public type="style" name="Widget.Quantum.ImageButton" /> @@ -2359,7 +2367,6 @@ <public type="style" name="Widget.Quantum.Light.EditText" /> <public type="style" name="Widget.Quantum.Light.ExpandableListView" /> <public type="style" name="Widget.Quantum.Light.FastScroll" /> - <public type="style" name="Widget.Quantum.Light.FragmentBreadCrumbs" /> <public type="style" name="Widget.Quantum.Light.GridView" /> <public type="style" name="Widget.Quantum.Light.HorizontalScrollView" /> <public type="style" name="Widget.Quantum.Light.ImageButton" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 891265f..fd57c5e 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -280,28 +280,28 @@ please see styles_device_defaults.xml. <style name="TextAppearance.StatusBar.Quantum"> </style> <style name="TextAppearance.StatusBar.Quantum.EventContent"> - <item name="android:textColor">#888888</item> + <item name="android:textColor">#90000000</item> <item name="android:textSize">@dimen/notification_text_size</item> </style> <style name="TextAppearance.StatusBar.Quantum.EventContent.Title"> - <item name="android:textColor">#000000</item> - <item name="android:fontFamily">sans-serif-light</item> + <item name="android:textColor">#DD000000</item> <item name="android:textSize">@dimen/notification_title_text_size</item> - <item name="android:textStyle">bold</item> </style> <style name="TextAppearance.StatusBar.Quantum.EventContent.Line2"> <item name="android:textSize">@dimen/notification_subtext_size</item> </style> <style name="TextAppearance.StatusBar.Quantum.EventContent.Info"> <item name="android:textSize">@dimen/notification_subtext_size</item> - <item name="android:textColor">#888888</item> </style> <style name="TextAppearance.StatusBar.Quantum.EventContent.Time"> <item name="android:textSize">@dimen/notification_subtext_size</item> - <item name="android:textColor">#888888</item> </style> <style name="TextAppearance.StatusBar.Quantum.EventContent.Emphasis"> - <item name="android:textColor">#555555</item> + <item name="android:textColor">#66000000</item> + </style> + + <style name="Widget.StatusBar.Quantum.ProgressBar" + parent="Widget.Quantum.Light.ProgressBar.Horizontal"> </style> <style name="TextAppearance.Small.CalendarViewWeekDayView"> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 60e06ce..609a0f3 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -91,7 +91,8 @@ easier. <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Quantum.CompoundButton.Switch"/> <style name="Widget.DeviceDefault.ExpandableListView.White" parent="Widget.Quantum.ExpandableListView.White"/> <style name="Widget.DeviceDefault.FastScroll" parent="Widget.Quantum.FastScroll"/> - <style name="Widget.DeviceDefault.FragmentBreadCrumbs" parent="Widget.Quantum.FragmentBreadCrumbs"/> + <!-- The FragmentBreadCrumbs widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). --> + <style name="Widget.DeviceDefault.FragmentBreadCrumbs" parent="Widget.Holo.FragmentBreadCrumbs"/> <style name="Widget.DeviceDefault.Gallery" parent="Widget.Quantum.Gallery"/> <style name="Widget.DeviceDefault.GestureOverlayView" parent="Widget.Quantum.GestureOverlayView"/> <style name="Widget.DeviceDefault.ImageWell" parent="Widget.Quantum.ImageWell"/> @@ -130,7 +131,8 @@ easier. <style name="Widget.DeviceDefault.Light.EditText" parent="Widget.Quantum.Light.EditText"/> <style name="Widget.DeviceDefault.Light.ExpandableListView" parent="Widget.Quantum.Light.ExpandableListView"/> <style name="Widget.DeviceDefault.Light.FastScroll" parent="Widget.Quantum.Light.FastScroll"/> - <style name="Widget.DeviceDefault.Light.FragmentBreadCrumbs" parent="Widget.Quantum.Light.FragmentBreadCrumbs"/> + <!-- The FragmentBreadCrumbs widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). --> + <style name="Widget.DeviceDefault.Light.FragmentBreadCrumbs" parent="Widget.Holo.Light.FragmentBreadCrumbs"/> <style name="Widget.DeviceDefault.Light.GridView" parent="Widget.Quantum.Light.GridView"/> <style name="Widget.DeviceDefault.Light.ImageButton" parent="Widget.Quantum.Light.ImageButton"/> <style name="Widget.DeviceDefault.Light.ListView" parent="Widget.Quantum.Light.ListView"/> diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index e693673..a49b89a 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -474,7 +474,6 @@ please see styles_device_defaults.xml. </style> <style name="Widget.Quantum.ExpandableListView.White"/> - <style name="Widget.Quantum.FragmentBreadCrumbs" parent="Widget.FragmentBreadCrumbs"/> <style name="Widget.Quantum.Gallery" parent="Widget.Gallery"/> <style name="Widget.Quantum.GestureOverlayView" parent="Widget.GestureOverlayView"/> <style name="Widget.Quantum.GridView" parent="Widget.GridView"/> @@ -820,7 +819,6 @@ please see styles_device_defaults.xml. <style name="Widget.Quantum.Light.EditText" parent="Widget.Quantum.EditText"/> <style name="Widget.Quantum.Light.ExpandableListView" parent="Widget.Quantum.ExpandableListView"/> <style name="Widget.Quantum.Light.ExpandableListView.White" parent="Widget.Quantum.ExpandableListView.White"/> - <style name="Widget.Quantum.Light.FragmentBreadCrumbs" parent="Widget.Quantum.FragmentBreadCrumbs"/> <style name="Widget.Quantum.Light.Gallery" parent="Widget.Quantum.Gallery"/> <style name="Widget.Quantum.Light.GestureOverlayView" parent="Widget.Quantum.GestureOverlayView"/> <style name="Widget.Quantum.Light.GridView" parent="Widget.Quantum.GridView"/> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 84c9023..2f0ac49 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1209,16 +1209,7 @@ <java-symbol type="layout" name="zoom_container" /> <java-symbol type="layout" name="zoom_controls" /> <java-symbol type="layout" name="zoom_magnify" /> - <java-symbol type="layout" name="notification_action" /> - <java-symbol type="layout" name="notification_action_tombstone" /> <java-symbol type="layout" name="notification_intruder_content" /> - <java-symbol type="layout" name="notification_template_base" /> - <java-symbol type="layout" name="notification_template_big_base" /> - <java-symbol type="layout" name="notification_template_big_picture" /> - <java-symbol type="layout" name="notification_template_big_text" /> - <java-symbol type="layout" name="notification_template_part_time" /> - <java-symbol type="layout" name="notification_template_part_chronometer" /> - <java-symbol type="layout" name="notification_template_inbox" /> <java-symbol type="layout" name="sms_short_code_confirmation_dialog" /> <java-symbol type="layout" name="action_bar_up_container" /> <java-symbol type="layout" name="app_not_authorized" /> @@ -1667,8 +1658,10 @@ <java-symbol type="layout" name="notification_template_quantum_big_picture" /> <java-symbol type="layout" name="notification_template_quantum_big_text" /> <java-symbol type="layout" name="notification_template_quantum_inbox" /> + <java-symbol type="layout" name="notification_template_icon_group" /> <java-symbol type="color" name="notification_action_legacy_color_filter" /> <java-symbol type="color" name="notification_icon_bg_color" /> + <java-symbol type="drawable" name="notification_icon_legacy_bg" /> <java-symbol type="drawable" name="notification_icon_legacy_bg_inset" /> <java-symbol type="drawable" name="notification_quantum_bg_dim" /> <java-symbol type="drawable" name="notification_quantum_bg" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 1d9bbae..743ad61 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -189,6 +189,9 @@ please see themes_device_defaults.xml. <item name="windowCloseOnTouchOutside">false</item> <item name="windowTranslucentStatus">false</item> <item name="windowTranslucentNavigation">false</item> + <item name="windowDrawsSystemBarBackgrounds">false</item> + <item name="statusBarColor">@android:color/black</item> + <item name="navigationBarColor">@android:color/black</item> <!-- Define these here; ContextThemeWrappers around themes that define them should always clear these values. --> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index dbc3d9e..aee35ef 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -174,7 +174,7 @@ easier. <item name="actionBarTabTextStyle">@style/Widget.DeviceDefault.ActionBar.TabText</item> <item name="actionModeStyle">@style/Widget.DeviceDefault.ActionMode</item> <item name="actionModeCloseButtonStyle">@style/Widget.DeviceDefault.ActionButton.CloseMode</item> - <item name="actionBarStyle">@style/Widget.DeviceDefault.ActionBar</item> + <item name="actionBarStyle">@style/Widget.DeviceDefault.ActionBar.Solid</item> <item name="actionModePopupWindowStyle">@style/Widget.DeviceDefault.PopupWindow.ActionMode</item> <item name="buttonBarStyle">@style/DeviceDefault.ButtonBar</item> @@ -435,7 +435,7 @@ easier. <item name="actionBarTabTextStyle">@style/Widget.DeviceDefault.Light.ActionBar.TabText</item> <item name="actionModeStyle">@style/Widget.DeviceDefault.Light.ActionMode</item> <item name="actionModeCloseButtonStyle">@style/Widget.DeviceDefault.Light.ActionButton.CloseMode</item> - <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar</item> + <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item> <item name="actionModePopupWindowStyle">@style/Widget.DeviceDefault.Light.PopupWindow.ActionMode</item> <item name="buttonBarStyle">@style/DeviceDefault.Light.ButtonBar</item> @@ -469,18 +469,7 @@ easier. <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an inverse color profile. --> - <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Quantum.Light.DarkActionBar" > - <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid.Inverse</item> - <item name="actionDropDownStyle">@style/Widget.DeviceDefault.Spinner.DropDown.ActionBar</item> - <item name="actionButtonStyle">@style/Widget.DeviceDefault.ActionButton</item> - <item name="actionOverflowButtonStyle">@style/Widget.DeviceDefault.ActionButton.Overflow</item> - <item name="actionBarTabStyle">@style/Widget.DeviceDefault.Light.ActionBar.TabView.Inverse</item> - <item name="actionBarTabBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.TabBar.Inverse</item> - <item name="actionBarTabTextStyle">@style/Widget.DeviceDefault.Light.ActionBar.TabText.Inverse</item> - <item name="actionModeStyle">@style/Widget.DeviceDefault.Light.ActionMode.Inverse</item> - <item name="actionModeCloseButtonStyle">@style/Widget.DeviceDefault.ActionButton.CloseMode</item> - <item name="actionModePopupWindowStyle">@style/Widget.DeviceDefault.PopupWindow.ActionMode</item> - </style> + <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Quantum.Light.DarkActionBar" /> <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar --> <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Quantum.Light.NoActionBar" /> diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml index 6f93c829..e7cf9da 100644 --- a/core/res/res/values/themes_quantum.xml +++ b/core/res/res/values/themes_quantum.xml @@ -161,6 +161,9 @@ please see themes_device_defaults.xml. <item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item> <item name="windowActionBar">true</item> <item name="windowActionModeOverlay">false</item> + <item name="windowDrawsSystemBarBackgrounds">true</item> + <item name="statusBarColor">?attr/colorPrimaryDark</item> + <item name="navigationBarColor">?attr/colorPrimaryDark</item> <!-- Dialog attributes --> <item name="dialogTheme">@style/Theme.Quantum.Dialog</item> @@ -266,7 +269,6 @@ please see themes_device_defaults.xml. <item name="popupMenuStyle">@style/Widget.Quantum.PopupMenu</item> <item name="stackViewStyle">@style/Widget.Quantum.StackView</item> <item name="activityChooserViewStyle">@style/Widget.Quantum.ActivityChooserView</item> - <item name="fragmentBreadCrumbsStyle">@style/Widget.Quantum.FragmentBreadCrumbs</item> <!-- Preference styles --> <item name="preferenceScreenStyle">@style/Preference.Quantum.PreferenceScreen</item> @@ -303,8 +305,8 @@ please see themes_device_defaults.xml. <item name="actionBarStyle">@style/Widget.Quantum.ActionBar.Solid</item> <item name="actionBarSize">@dimen/action_bar_default_height_quantum</item> <item name="actionModePopupWindowStyle">@style/Widget.Quantum.PopupWindow.ActionMode</item> - <item name="actionBarWidgetTheme">@null</item> - <item name="actionBarTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item> + <item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item> + <item name="actionBarTheme">@null</item> <item name="actionBarItemBackground">@drawable/item_background_quantum</item> <item name="actionModeCutDrawable">@drawable/ic_menu_cut_quantum</item> @@ -501,6 +503,9 @@ please see themes_device_defaults.xml. <item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item> <item name="windowActionBar">true</item> <item name="windowActionModeOverlay">false</item> + <item name="windowDrawsSystemBarBackgrounds">true</item> + <item name="statusBarColor">?attr/colorPrimaryDark</item> + <item name="navigationBarColor">?attr/colorPrimaryDark</item> <!-- Dialog attributes --> <item name="dialogTheme">@style/Theme.Quantum.Light.Dialog</item> @@ -606,7 +611,6 @@ please see themes_device_defaults.xml. <item name="popupMenuStyle">@style/Widget.Quantum.Light.PopupMenu</item> <item name="stackViewStyle">@style/Widget.Quantum.Light.StackView</item> <item name="activityChooserViewStyle">@style/Widget.Quantum.Light.ActivityChooserView</item> - <item name="fragmentBreadCrumbsStyle">@style/Widget.Quantum.Light.FragmentBreadCrumbs</item> <!-- Preference styles --> <item name="preferenceScreenStyle">@style/Preference.Quantum.PreferenceScreen</item> @@ -646,8 +650,8 @@ please see themes_device_defaults.xml. <item name="actionBarStyle">@style/Widget.Quantum.Light.ActionBar.Solid</item> <item name="actionBarSize">@dimen/action_bar_default_height_quantum</item> <item name="actionModePopupWindowStyle">@style/Widget.Quantum.Light.PopupWindow.ActionMode</item> - <item name="actionBarWidgetTheme">@null</item> - <item name="actionBarTheme">@style/ThemeOverlay.Quantum.Light.ActionBarWidget</item> + <item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item> + <item name="actionBarTheme">@null</item> <item name="actionBarItemBackground">@drawable/item_background_quantum</item> <item name="actionModeCutDrawable">@drawable/ic_menu_cut_quantum</item> @@ -718,29 +722,93 @@ please see themes_device_defaults.xml. <item name="colorButtonPressed">@color/quantum_grey_500</item> </style> + <!-- Variant of the quantum (light) theme that has a solid (opaque) action bar + with an inverse color profile. The dark action bar sharply stands out against + the light content. --> + <style name="Theme.Quantum.Light.DarkActionBar"> + <item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item> + <item name="actionBarTheme">@style/ThemeOverlay.Quantum.Dark</item> + </style> + <style name="ThemeOverlay" /> <style name="ThemeOverlay.Quantum" /> - <style name="ThemeOverlay.Quantum.Light" /> - <!-- Variant of the quantum theme that replaces the activated control color - (which by default is identical to the action bar background color) with - the normal control color . --> - <style name="ThemeOverlay.Quantum.ActionBarWidget"> - <item name="colorControlActivated">?attr/colorControlNormal</item> + <!-- Theme overlay that replaces colors with their light versions but preserves + the value of colorAccent, colorPrimary and its variants. --> + <style name="ThemeOverlay.Quantum.Light"> + <item name="colorForeground">@color/bright_foreground_quantum_light</item> + <item name="colorForegroundInverse">@color/bright_foreground_quantum_dark</item> + <item name="colorBackground">@color/background_quantum_light</item> + <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_quantum_light</item> + + <item name="textColorPrimary">@color/primary_text_quantum_light</item> + <item name="textColorPrimaryInverse">@color/primary_text_quantum_dark</item> + <item name="textColorSecondary">@color/secondary_text_quantum_light</item> + <item name="textColorSecondaryInverse">@color/secondary_text_quantum_dark</item> + <item name="textColorTertiary">@color/tertiary_text_quantum_light</item> + <item name="textColorTertiaryInverse">@color/tertiary_text_quantum_dark</item> + <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_quantum_light</item> + <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_quantum_dark</item> + <item name="textColorHint">@color/hint_foreground_quantum_light</item> + <item name="textColorHintInverse">@color/hint_foreground_quantum_dark</item> + <item name="textColorHighlight">@color/highlighted_text_quantum_light</item> + <item name="textColorHighlightInverse">@color/highlighted_text_quantum_dark</item> + <item name="textColorLink">@color/quantum_teal_500</item> + <item name="textColorLinkInverse">@color/quantum_teal_500</item> + <item name="textColorSearchUrl">@color/search_url_text_quantum_light</item> + <item name="textColorAlertDialogListItem">@color/primary_text_quantum_light</item> + + <item name="textCheckMark">@drawable/indicator_check_mark_light</item> + <item name="textCheckMarkInverse">@drawable/indicator_check_mark_dark</item> + + <item name="windowBackground">@color/background_quantum_light</item> + + <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item> + <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item> + + <item name="colorButtonNormal">@color/quantum_grey_100</item> </style> - <!-- Variant of the quantum (light) theme that replaces the activated control - color (which by default is identical to the action bar background color) - with the normal control color . --> - <style name="ThemeOverlay.Quantum.Light.ActionBarWidget"> - <item name="colorControlActivated">?attr/colorControlNormal</item> + <!-- Theme overlay that replaces colors with their dark versions but preserves + the value of colorAccent, colorPrimary and its variants. --> + <style name="ThemeOverlay.Quantum.Dark"> + <item name="colorForeground">@color/bright_foreground_quantum_dark</item> + <item name="colorForegroundInverse">@color/bright_foreground_quantum_light</item> + <item name="colorBackground">@color/background_quantum_dark</item> + <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_quantum_dark</item> + + <item name="textColorPrimary">@color/primary_text_quantum_dark</item> + <item name="textColorPrimaryInverse">@color/primary_text_quantum_light</item> + <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_quantum_dark</item> + <item name="textColorSecondary">@color/secondary_text_quantum_dark</item> + <item name="textColorSecondaryInverse">@color/secondary_text_quantum_light</item> + <item name="textColorTertiary">@color/tertiary_text_quantum_dark</item> + <item name="textColorTertiaryInverse">@color/tertiary_text_quantum_light</item> + <item name="textColorHint">@color/hint_foreground_quantum_dark</item> + <item name="textColorHintInverse">@color/hint_foreground_quantum_light</item> + <item name="textColorHighlight">@color/highlighted_text_quantum_dark</item> + <item name="textColorHighlightInverse">@color/highlighted_text_quantum_light</item> + <item name="textColorLink">@color/quantum_teal_500</item> + <item name="textColorLinkInverse">@color/quantum_teal_500</item> + <item name="textColorSearchUrl">@color/search_url_text_quantum_dark</item> + <item name="textColorAlertDialogListItem">@color/primary_text_quantum_dark</item> + + <item name="textCheckMark">@drawable/indicator_check_mark_dark</item> + <item name="textCheckMarkInverse">@drawable/indicator_check_mark_light</item> + + <item name="windowBackground">@color/background_quantum_dark</item> + + <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item> + <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item> + + <item name="colorButtonNormal">@color/quantum_grey_700</item> </style> - <!-- Variant of the quantum (light) theme that has a solid (opaque) action bar - with an inverse color profile. The dark action bar sharply stands out against - the light content. --> - <style name="Theme.Quantum.Light.DarkActionBar"> - <!-- TODO --> + <!-- Theme overlay that replaces the activated control color (which by default + is identical to the action bar background color) with the normal control + color. --> + <style name="ThemeOverlay.Quantum.ActionBarWidget"> + <item name="colorControlActivated">?attr/colorControlNormal</item> </style> <!-- Variant of the quantum (dark) theme with no action bar. --> diff --git a/core/tests/coretests/src/android/view/VelocityTest.java b/core/tests/coretests/src/android/view/VelocityTest.java index fb28e1e..12abf3e 100644 --- a/core/tests/coretests/src/android/view/VelocityTest.java +++ b/core/tests/coretests/src/android/view/VelocityTest.java @@ -14,14 +14,12 @@ * limitations under the License. */ -package com.android.frameworktest.view; +package android.view; import junit.framework.Assert; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.MediumTest; -import android.view.MotionEvent; -import android.view.VelocityTracker; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; @@ -30,7 +28,7 @@ import android.view.animation.LinearInterpolator; /** * Exercises {@link android.view.VelocityTracker} to compute correct velocity.<br> * To launch this test, use :<br> - * <code>./development/testrunner/runtest.py framework -c com.android.frameworktest.view.VelocityTest</code> + * <code>./development/testrunner/runtest.py framework -c android.view.VelocityTest</code> */ public class VelocityTest extends InstrumentationTestCase { diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/AndroidManifest.xml b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/AndroidManifest.xml index d69a63b..6bd47c2 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/AndroidManifest.xml +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/AndroidManifest.xml @@ -7,6 +7,7 @@ <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18"/> <application + android:name=".TestApplication" android:label="multidexlegacytestapp" > <activity diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Annotated.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Annotated.java new file mode 100644 index 0000000..603c573 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Annotated.java @@ -0,0 +1,22 @@ +/* + * 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.multidexlegacytestapp; + +@AnnotationWithEnum2(ReferencedByAnnotationWithOtherReferences.A) +public class Annotated { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Annotated2.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Annotated2.java new file mode 100644 index 0000000..eed6875 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Annotated2.java @@ -0,0 +1,22 @@ +/* + * 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.multidexlegacytestapp; + +@AnnotationWithClass(ReferencedByClassInAnnotation.class) +public class Annotated2 { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithClass.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithClass.java new file mode 100644 index 0000000..378a024 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithClass.java @@ -0,0 +1,27 @@ +/* + * 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.multidexlegacytestapp; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface AnnotationWithClass { + + Class<?> value(); + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithEnum.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithEnum.java new file mode 100644 index 0000000..15855d5 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithEnum.java @@ -0,0 +1,27 @@ +/* + * 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.multidexlegacytestapp; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface AnnotationWithEnum { + + ReferencedByAnnotation value(); + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithEnum2.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithEnum2.java new file mode 100644 index 0000000..ce37b18 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/AnnotationWithEnum2.java @@ -0,0 +1,27 @@ +/* + * 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.multidexlegacytestapp; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface AnnotationWithEnum2 { + + ReferencedByAnnotationWithOtherReferences value(); + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/InterfaceWithEnum.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/InterfaceWithEnum.java new file mode 100644 index 0000000..085c0a3 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/InterfaceWithEnum.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.multidexlegacytestapp; + +public interface InterfaceWithEnum { + + ReferencedByInterface getEnum(); + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/MainActivity.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/MainActivity.java index 3228825..5bc03f1 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/MainActivity.java +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/MainActivity.java @@ -17,7 +17,6 @@ package com.android.multidexlegacytestapp; import android.app.Activity; import android.os.Bundle; -import android.support.multidex.MultiDex; import android.util.Log; import android.widget.TextView; @@ -36,9 +35,6 @@ public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { - Log.i(TAG, "onCreate"); - MultiDex.install(getApplicationContext()); - Log.i(TAG, "Multi dex installation done."); super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); int value = getValue(); @@ -94,4 +90,8 @@ public class MainActivity extends Activity { return value; } + public int getAnnotation2Value() { + return ((AnnotationWithEnum2) TestApplication.annotation2).value().get(); + } + } diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByAnnotation.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByAnnotation.java new file mode 100644 index 0000000..0aa6995 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByAnnotation.java @@ -0,0 +1,24 @@ +/* + * 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.multidexlegacytestapp; + +public enum ReferencedByAnnotation { + + A, + B; + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByAnnotationWithOtherReferences.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByAnnotationWithOtherReferences.java new file mode 100644 index 0000000..8cc0d9d --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByAnnotationWithOtherReferences.java @@ -0,0 +1,38 @@ +/* + * 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.multidexlegacytestapp; + +public enum ReferencedByAnnotationWithOtherReferences { + + A { + private ReferencedByEnum a = new ReferencedByEnum(); + @Override + public int get() { + return a.hashCode(); + } + }, + B { + private ReferencedByEnum b = new ReferencedByEnum(); + @Override + public int get() { + return b.hashCode(); + } + }; + + + public abstract int get(); +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByClassInAnnotation.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByClassInAnnotation.java new file mode 100644 index 0000000..06aa560 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByClassInAnnotation.java @@ -0,0 +1,38 @@ +/* + * 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.multidexlegacytestapp; + +public enum ReferencedByClassInAnnotation { + + A { + private ReferencedByEnum a = new ReferencedByEnum(); + @Override + public int get() { + return a.hashCode(); + } + }, + B { + private ReferencedByEnum b = new ReferencedByEnum(); + @Override + public int get() { + return b.hashCode(); + } + }; + + + public abstract int get(); +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByEnum.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByEnum.java new file mode 100644 index 0000000..1ceb3d3 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByEnum.java @@ -0,0 +1,21 @@ +/* + * 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.multidexlegacytestapp; + +public class ReferencedByEnum { + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByInterface.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByInterface.java new file mode 100644 index 0000000..17f1449 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/ReferencedByInterface.java @@ -0,0 +1,24 @@ +/* + * 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.multidexlegacytestapp; + +public enum ReferencedByInterface { + + A, + B; + +} diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Test.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Test.java index 59cac07..bbdd3e5 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Test.java +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/Test.java @@ -17,6 +17,11 @@ package com.android.multidexlegacytestapp; import android.test.ActivityInstrumentationTestCase2; +/** + * Run the tests with: <code>adb shell am instrument -w + com.android.multidexlegacytestapp/android.test.InstrumentationTestRunner +</code> + */ public class Test extends ActivityInstrumentationTestCase2<MainActivity> { public Test() { super(MainActivity.class); @@ -25,4 +30,23 @@ public class Test extends ActivityInstrumentationTestCase2<MainActivity> { public void testAllClassesAvailable() { assertEquals(3366, getActivity().getValue()); } + + public void testAnnotation() { + assertEquals(ReferencedByAnnotation.B, + ((AnnotationWithEnum) TestApplication.annotation).value()); + assertEquals(ReferencedByAnnotation.B, + ((AnnotationWithEnum) TestApplication.getAnnotationWithEnum()).value()); + // Just to verify that it doesn't crash + getActivity().getAnnotation2Value(); + + assertEquals(ReferencedByClassInAnnotation.class, + ((AnnotationWithClass) TestApplication.annotation3).value()); + // Just to verify that it doesn't crash + ReferencedByClassInAnnotation.A.get(); + } + + public void testInterface() { + assertEquals(InterfaceWithEnum.class, + TestApplication.interfaceClass); + } } diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/TestApplication.java b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/TestApplication.java new file mode 100644 index 0000000..c52ad29 --- /dev/null +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/src/com/android/multidexlegacytestapp/TestApplication.java @@ -0,0 +1,44 @@ +/* + * 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.multidexlegacytestapp; + +import android.support.multidex.MultiDexApplication; + +import java.lang.annotation.Annotation; + +@AnnotationWithEnum(ReferencedByAnnotation.B) +public class TestApplication extends MultiDexApplication { + + public static Annotation annotation = getAnnotationWithEnum(); + public static Annotation annotation2 = getSoleAnnotation(Annotated.class); + public static Annotation annotation3 = getSoleAnnotation(Annotated2.class); + public static Class<?> interfaceClass = InterfaceWithEnum.class; + + public static Annotation getAnnotationWithEnum() { + return getSoleAnnotation(TestApplication.class); + } + + public static Annotation getSoleAnnotation(Class<?> annotated) { + Annotation[] annot = annotated.getAnnotations(); + if (annot.length == 1) { + return annot[0]; + } + + throw new AssertionError(); + } + +} diff --git a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java index 59a6314..7d72f3e 100644 --- a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java +++ b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java @@ -52,18 +52,21 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { public void testBuilder() throws Exception { final int SELECTION_START = 30; final int SELECTION_END = 40; - final int CANDIDATES_START = 32; - final int CANDIDATES_END = 33; + final int COMPOSING_TEXT_START = 32; + final String COMPOSING_TEXT = "test"; final float INSERTION_MARKER_HORIZONTAL = 10.5f; final float INSERTION_MARKER_TOP = 100.1f; final float INSERTION_MARKER_BASELINE = 110.4f; final float INSERTION_MARKER_BOTOM = 111.0f; + final int CHAR_INDEX = 32; + final char CHAR_VALUE = 'X'; + final char DEFAULT_CHAR_VALUE = '!'; Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX); TRANSFORM_MATRIX.setScale(10.0f, 20.0f); final CursorAnchorInfoBuilder builder = new CursorAnchorInfoBuilder(); builder.setSelectionRange(SELECTION_START, SELECTION_END) - .setCandidateRange(CANDIDATES_START, CANDIDATES_END) + .setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT) .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP, INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM) .setMatrix(TRANSFORM_MATRIX); @@ -77,8 +80,8 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { final CursorAnchorInfo info = builder.build(); assertEquals(SELECTION_START, info.getSelectionStart()); assertEquals(SELECTION_END, info.getSelectionEnd()); - assertEquals(CANDIDATES_START, info.getCandidatesStart()); - assertEquals(CANDIDATES_END, info.getCandidatesEnd()); + assertEquals(COMPOSING_TEXT_START, info.getComposingTextStart()); + assertEquals(COMPOSING_TEXT, info.getComposingText()); assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal()); assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop()); assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline()); @@ -93,8 +96,8 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { final CursorAnchorInfo info2 = builder.build(); assertEquals(SELECTION_START, info2.getSelectionStart()); assertEquals(SELECTION_END, info2.getSelectionEnd()); - assertEquals(CANDIDATES_START, info2.getCandidatesStart()); - assertEquals(CANDIDATES_END, info2.getCandidatesEnd()); + assertEquals(COMPOSING_TEXT_START, info2.getComposingTextStart()); + assertEquals(COMPOSING_TEXT, info2.getComposingText()); assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal()); assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop()); assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline()); @@ -111,8 +114,8 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { final CursorAnchorInfo info3 = cloneViaParcel(info2); assertEquals(SELECTION_START, info3.getSelectionStart()); assertEquals(SELECTION_END, info3.getSelectionEnd()); - assertEquals(CANDIDATES_START, info3.getCandidatesStart()); - assertEquals(CANDIDATES_END, info3.getCandidatesEnd()); + assertEquals(COMPOSING_TEXT_START, info3.getComposingTextStart()); + assertEquals(COMPOSING_TEXT, info3.getComposingText()); assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal()); assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop()); assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline()); @@ -128,13 +131,40 @@ public class CursorAnchorInfoTest extends InstrumentationTestCase { final CursorAnchorInfo uninitializedInfo = builder.build(); assertEquals(-1, uninitializedInfo.getSelectionStart()); assertEquals(-1, uninitializedInfo.getSelectionEnd()); - assertEquals(-1, uninitializedInfo.getCandidatesStart()); - assertEquals(-1, uninitializedInfo.getCandidatesEnd()); + assertEquals(-1, uninitializedInfo.getComposingTextStart()); + assertNull(uninitializedInfo.getComposingText()); assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal()); assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop()); assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline()); assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom()); - assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix()); + } + + @SmallTest + public void testMatrixIsCopied() throws Exception { + final Matrix MATRIX1 = new Matrix(); + MATRIX1.setTranslate(10.0f, 20.0f); + final Matrix MATRIX2 = new Matrix(); + MATRIX2.setTranslate(110.0f, 120.0f); + final Matrix MATRIX3 = new Matrix(); + MATRIX3.setTranslate(210.0f, 220.0f); + final Matrix matrix = new Matrix(); + final CursorAnchorInfoBuilder builder = new CursorAnchorInfoBuilder(); + + matrix.set(MATRIX1); + builder.setMatrix(matrix); + matrix.postRotate(90.0f); + + final CursorAnchorInfo firstInstance = builder.build(); + assertEquals(MATRIX1, firstInstance.getMatrix()); + matrix.set(MATRIX2); + builder.setMatrix(matrix); + final CursorAnchorInfo secondInstance = builder.build(); + assertEquals(MATRIX1, firstInstance.getMatrix()); + assertEquals(MATRIX2, secondInstance.getMatrix()); + + matrix.set(MATRIX3); + assertEquals(MATRIX1, firstInstance.getMatrix()); + assertEquals(MATRIX2, secondInstance.getMatrix()); } @SmallTest diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index 97c1b33..452c575 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -33,10 +33,14 @@ ALL_MODULES.$(1).INSTALLED := \ endef ########################################## -# We may only afford small font footprint. +# The following fonts are distributed as symlink only. ########################################## $(eval $(call create-font-symlink,DroidSans.ttf,Roboto-Regular.ttf)) $(eval $(call create-font-symlink,DroidSans-Bold.ttf,Roboto-Bold.ttf)) +$(eval $(call create-font-symlink,DroidSerif-Regular.ttf,NotoSerif-Regular.ttf)) +$(eval $(call create-font-symlink,DroidSerif-Bold.ttf,NotoSerif-Bold.ttf)) +$(eval $(call create-font-symlink,DroidSerif-Italic.ttf,NotoSerif-Italic.ttf)) +$(eval $(call create-font-symlink,DroidSerif-BoldItalic.ttf,NotoSerif-BoldItalic.ttf)) ################################ # On space-constrained devices, we include a subset of fonts: @@ -45,14 +49,6 @@ droidsans_fallback_src := DroidSansFallback.ttf extra_font_files := DroidSans.ttf DroidSans-Bold.ttf else include $(CLEAR_VARS) -LOCAL_MODULE := DroidSansEthiopic-Regular.ttf -LOCAL_SRC_FILES := $(LOCAL_MODULE) -LOCAL_MODULE_CLASS := ETC -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(TARGET_OUT)/fonts -include $(BUILD_PREBUILT) - -include $(CLEAR_VARS) LOCAL_MODULE := MTLmr3m.ttf LOCAL_SRC_FILES := $(LOCAL_MODULE) LOCAL_MODULE_CLASS := ETC @@ -64,7 +60,6 @@ droidsans_fallback_src := DroidSansFallbackFull.ttf extra_font_files := \ DroidSans.ttf \ DroidSans-Bold.ttf \ - DroidSansEthiopic-Regular.ttf \ MTLmr3m.ttf endif # SMALLER_FONT_FOOTPRINT @@ -102,10 +97,6 @@ font_src_files := \ Roboto-Bold.ttf \ Roboto-Italic.ttf \ Roboto-BoldItalic.ttf \ - DroidSerif-Regular.ttf \ - DroidSerif-Bold.ttf \ - DroidSerif-Italic.ttf \ - DroidSerif-BoldItalic.ttf \ DroidSansMono.ttf \ Clockopia.ttf \ AndroidClock.ttf \ @@ -135,12 +126,6 @@ font_src_files += \ RobotoCondensed-BoldItalic.ttf \ RobotoCondensed-Light.ttf \ RobotoCondensed-LightItalic.ttf \ - DroidNaskh-Regular.ttf \ - DroidNaskhUI-Regular.ttf \ - DroidSansHebrew-Regular.ttf \ - DroidSansHebrew-Bold.ttf \ - DroidSansArmenian.ttf \ - DroidSansGeorgian.ttf \ AndroidEmoji.ttf endif # !MINIMAL_FONT diff --git a/data/fonts/DroidNaskh-Bold.ttf b/data/fonts/DroidNaskh-Bold.ttf Binary files differdeleted file mode 100644 index 14d8768..0000000 --- a/data/fonts/DroidNaskh-Bold.ttf +++ /dev/null diff --git a/data/fonts/DroidNaskh-Regular.ttf b/data/fonts/DroidNaskh-Regular.ttf Binary files differdeleted file mode 100644 index 03662f2..0000000 --- a/data/fonts/DroidNaskh-Regular.ttf +++ /dev/null diff --git a/data/fonts/DroidNaskhUI-Regular.ttf b/data/fonts/DroidNaskhUI-Regular.ttf Binary files differdeleted file mode 100644 index c961de2..0000000 --- a/data/fonts/DroidNaskhUI-Regular.ttf +++ /dev/null diff --git a/data/fonts/DroidSansArabic.ttf b/data/fonts/DroidSansArabic.ttf Binary files differdeleted file mode 100644 index bdefaac..0000000 --- a/data/fonts/DroidSansArabic.ttf +++ /dev/null diff --git a/data/fonts/DroidSansArmenian.ttf b/data/fonts/DroidSansArmenian.ttf Binary files differdeleted file mode 100644 index 6fafa54..0000000 --- a/data/fonts/DroidSansArmenian.ttf +++ /dev/null diff --git a/data/fonts/DroidSansEthiopic-Bold.ttf b/data/fonts/DroidSansEthiopic-Bold.ttf Binary files differdeleted file mode 100644 index e06cac2..0000000 --- a/data/fonts/DroidSansEthiopic-Bold.ttf +++ /dev/null diff --git a/data/fonts/DroidSansEthiopic-Regular.ttf b/data/fonts/DroidSansEthiopic-Regular.ttf Binary files differdeleted file mode 100644 index 0adcbbe..0000000 --- a/data/fonts/DroidSansEthiopic-Regular.ttf +++ /dev/null diff --git a/data/fonts/DroidSansGeorgian.ttf b/data/fonts/DroidSansGeorgian.ttf Binary files differdeleted file mode 100644 index 3a2e9fb..0000000 --- a/data/fonts/DroidSansGeorgian.ttf +++ /dev/null diff --git a/data/fonts/DroidSansHebrew-Bold.ttf b/data/fonts/DroidSansHebrew-Bold.ttf Binary files differdeleted file mode 100644 index c1acb38..0000000 --- a/data/fonts/DroidSansHebrew-Bold.ttf +++ /dev/null diff --git a/data/fonts/DroidSansHebrew-Regular.ttf b/data/fonts/DroidSansHebrew-Regular.ttf Binary files differdeleted file mode 100644 index af6a58d..0000000 --- a/data/fonts/DroidSansHebrew-Regular.ttf +++ /dev/null diff --git a/data/fonts/DroidSerif-Bold.ttf b/data/fonts/DroidSerif-Bold.ttf Binary files differdeleted file mode 100644 index 16a914e..0000000 --- a/data/fonts/DroidSerif-Bold.ttf +++ /dev/null diff --git a/data/fonts/DroidSerif-BoldItalic.ttf b/data/fonts/DroidSerif-BoldItalic.ttf Binary files differdeleted file mode 100644 index 50324fc..0000000 --- a/data/fonts/DroidSerif-BoldItalic.ttf +++ /dev/null diff --git a/data/fonts/DroidSerif-Italic.ttf b/data/fonts/DroidSerif-Italic.ttf Binary files differdeleted file mode 100644 index bb2757c..0000000 --- a/data/fonts/DroidSerif-Italic.ttf +++ /dev/null diff --git a/data/fonts/DroidSerif-Regular.ttf b/data/fonts/DroidSerif-Regular.ttf Binary files differdeleted file mode 100644 index da0a2cc..0000000 --- a/data/fonts/DroidSerif-Regular.ttf +++ /dev/null diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml index ede7ef4..c2d5afe 100644 --- a/data/fonts/fallback_fonts.xml +++ b/data/fonts/fallback_fonts.xml @@ -31,23 +31,26 @@ <familyset> <family> <fileset> - <file variant="elegant">DroidNaskh-Regular.ttf</file> + <file variant="elegant">NotoNaskh-Regular.ttf</file> + <file variant="elegant">NotoNaskh-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file variant="compact">DroidNaskhUI-Regular.ttf</file> + <file variant="compact">NotoNaskhUI-Regular.ttf</file> + <file variant="compact">NotoNaskhUI-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>DroidSansEthiopic-Regular.ttf</file> + <file>NotoSansEthiopic-Regular.ttf</file> + <file>NotoSansEthiopic-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>DroidSansHebrew-Regular.ttf</file> - <file>DroidSansHebrew-Bold.ttf</file> + <file>NotoSansHebrew-Regular.ttf</file> + <file>NotoSansHebrew-Bold.ttf</file> </fileset> </family> <family> @@ -64,12 +67,14 @@ </family> <family> <fileset> - <file>DroidSansArmenian.ttf</file> + <file>NotoSansArmenian-Regular.ttf</file> + <file>NotoSansArmenian-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>DroidSansGeorgian.ttf</file> + <file>NotoSansGeorgian-Regular.ttf</file> + <file>NotoSansGeorgian-Bold.ttf</file> </fileset> </family> <family> @@ -84,6 +89,32 @@ <file variant="compact">NotoSansDevanagariUI-Bold.ttf</file> </fileset> </family> + <!-- Gujarati should come after Devanagari --> + <family> + <fileset> + <file variant="elegant">NotoSansGujarati-Regular.ttf</file> + <file variant="elegant">NotoSansGujarati-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file variant="compact">NotoSansGujaratiUI-Regular.ttf</file> + <file variant="compact">NotoSansGujaratiUI-Bold.ttf</file> + </fileset> + </family> + <!-- Gurmukhi should come after Devanagari --> + <family> + <fileset> + <file variant="elegant">NotoSansGurmukhi-Regular.ttf</file> + <file variant="elegant">NotoSansGurmukhi-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file variant="compact">NotoSansGurmukhiUI-Regular.ttf</file> + <file variant="compact">NotoSansGurmukhiUI-Bold.ttf</file> + </fileset> + </family> <family> <fileset> <file variant="elegant">NotoSansTamil-Regular.ttf</file> @@ -146,6 +177,12 @@ </family> <family> <fileset> + <file>NotoSansSinhala-Regular.ttf</file> + <file>NotoSansSinhala-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> <file variant="elegant">NotoSansKhmer-Regular.ttf</file> <file variant="elegant">NotoSansKhmer-Bold.ttf</file> </fileset> @@ -170,18 +207,24 @@ </family> <family> <fileset> - <file>NanumGothic.ttf</file> + <file variant="elegant">NotoSansMyanmar-Regular.ttf</file> + <file variant="elegant">NotoSansMyanmar-Bold.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file variant="compact">NotoSansMyanmarUI-Regular.ttf</file> + <file variant="compact">NotoSansMyanmarUI-Bold.ttf</file> </fileset> </family> <family> <fileset> - <file>Padauk-book.ttf</file> - <file>Padauk-bookbold.ttf</file> + <file>NanumGothic.ttf</file> </fileset> </family> <family> <fileset> - <file>NotoSansSymbols-Regular.ttf</file> + <file>NotoSansSymbols-Regular-Subsetted.ttf</file> </fileset> </family> <family> diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index 293ecc8..2312a04 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -34,17 +34,7 @@ PRODUCT_PACKAGES := \ RobotoCondensed-BoldItalic.ttf \ RobotoCondensed-Light.ttf \ RobotoCondensed-LightItalic.ttf \ - DroidNaskh-Regular.ttf \ - DroidNaskhUI-Regular.ttf \ - DroidSansHebrew-Regular.ttf \ - DroidSansHebrew-Bold.ttf \ - DroidSerif-Regular.ttf \ - DroidSerif-Bold.ttf \ - DroidSerif-Italic.ttf \ - DroidSerif-BoldItalic.ttf \ DroidSansMono.ttf \ - DroidSansArmenian.ttf \ - DroidSansGeorgian.ttf \ AndroidEmoji.ttf \ Clockopia.ttf \ AndroidClock.ttf \ diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml index a8d23ee..97b7fd8 100644 --- a/data/fonts/system_fonts.xml +++ b/data/fonts/system_fonts.xml @@ -89,10 +89,10 @@ <name>ITC Stone Serif</name> </nameset> <fileset> - <file>DroidSerif-Regular.ttf</file> - <file>DroidSerif-Bold.ttf</file> - <file>DroidSerif-Italic.ttf</file> - <file>DroidSerif-BoldItalic.ttf</file> + <file>NotoSerif-Regular.ttf</file> + <file>NotoSerif-Bold.ttf</file> + <file>NotoSerif-Italic.ttf</file> + <file>NotoSerif-BoldItalic.ttf</file> </fileset> </family> diff --git a/docs/html/images/tools/studio_error_gradle5.png b/docs/html/images/tools/studio_error_gradle5.png Binary files differdeleted file mode 100644 index 13de607..0000000 --- a/docs/html/images/tools/studio_error_gradle5.png +++ /dev/null diff --git a/docs/html/images/tools/studio_error_supportlib.png b/docs/html/images/tools/studio_error_supportlib.png Binary files differdeleted file mode 100644 index 603b54c..0000000 --- a/docs/html/images/tools/studio_error_supportlib.png +++ /dev/null diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd index 8ea5e7e..a2c32f0 100644 --- a/docs/html/sdk/installing/studio.jd +++ b/docs/html/sdk/installing/studio.jd @@ -312,11 +312,6 @@ the Android SDK Manager.</p> </div> -<p>Also note that due to the update to Android Gradle Plugin 0.6, you will encounter errors when opening -existing projects. See the <a href="#Troubleshooting">Troubleshooting</a> notes below for -information about how to resolve them.</p> - - <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>. @@ -491,8 +486,7 @@ style="vertical-align:bottom;margin:0;height:19px" /> in the toolbar.</p> <li>Android Gradle plug-in updated to 0.5.0. <p class="caution"><strong>Caution:</strong> This new version is not backwards compatible. When opening a project that uses an older version of the plug-in, Studio will show an error - stating <strong>Gradle <project_name> project refresh failed.</strong> See <a - href="#Troubleshooting">Troubleshooting</a> below for details.</p> + stating <strong>Gradle <project_name> project refresh failed.</strong></p> <p>The updated Gradle plug-in includes the following changes:</p> <ul> <li>Fixed IDE model to contain the output file even if it's customized through the DSL. Also @@ -566,65 +560,7 @@ Check for updates</strong>).</p> <h2 id="Troubleshooting">Troubleshooting</h2> - -<div class="figure" style="width:330px"> -<img src="{@docRoot}images/tools/studio_error_gradle5.png" width="330"/> -<p class="img-caption"><strong>Figure 1.</strong> Error dialog when opening an existing project.</p> -</div> - -<h3>Error: Gradle project refresh failed</h3> - -<p>Android Studio 0.2.0 has updated the Gradle plug-in to 0.5.0, which is not backwards compatible. -When opening a project that uses an older version of the plug-in, Studio will display the error -shown in figure 1 in the upper right corner of the IDE. -To resolve the error, you must change the version of the Android Gradle plug-in to 0.5.0.</p> - -<ol> - <li>Click the link in the error dialog <strong>Search in build.gradle files</strong>. If the dialog -is no longer visible, click <strong>Event Log</strong> -<img src="{@docRoot}images/tools/studio_error_eventlog.png" -style="vertical-align:bottom;margin:0;height:19px"/> in the bottom-right corner of the IDE, -then click <strong>Search in build.gradle files</strong>.</li> - <li>Double-click the line under the <em>build.gradle</em> usage. For example: - <strong>classpath 'com.android.tools.build:gradle:0.4</strong>. This opens the project - <code>build.gradle</code> file.</li> - <li>Edit the <code>classpath</code> to change the gradle version to <code>0.5.+</code>. - For example: - <pre class="no-pretty-print"> -dependencies { - classpath 'com.android.tools.build:gradle:<strong>0.5.+</strong>' -} -</pre> - </li> - <li>Save the file and rebuild your project.</li> -</ol> - - - -<div class="figure" style="width:330px"> -<img src="{@docRoot}images/tools/studio_error_supportlib.png" width="330"/> -<p class="img-caption"><strong>Figure 2.</strong> Error dialog when creating a new project -or opening a project using the support library.</p> -</div> - -<h3>Error: Failed to import Gradle project</h3> - -<p>If, after updating to Android Studio 0.2.x and creating or opening a project, you receive an -error stating <em>"Could not find any version that matches -com.android.support:support-v4:13.0.+"</em>, then you must install the <strong>Android Support -Repository</strong>. This was likely caused because you're pointing Android Studio to an external -Android SDK location that does not have the new Maven repository included with Android Studio -0.2.x. This new Maven repository is used by the new build system for the Support Library, instead -of using the Support Library JAR files, so must be present in the SDK.</p> - - -<ol> - <li>Open the <strong>Android SDK Manager</strong>.</li> - <li>Expand the <strong>Extras</strong> directory -and install <strong>Android Support Repository</strong>.</li> -</ol> - -<p>If you've encountered other problems in Android Studio, look at the following page +<p>If you encounter problems in Android Studio, look at the following page for possible resolutions to known issues: <a href="http://tools.android.com/knownissues" >http://tools.android.com/knownissues</a>.</p> diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java new file mode 100644 index 0000000..a759a79 --- /dev/null +++ b/graphics/java/android/graphics/FontFamily.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.graphics; + +import java.io.File; + +/** + * A family of typefaces with different styles. + * + * @hide + */ +public class FontFamily { + /** + * @hide + */ + public long mNativePtr; + + public FontFamily() { + mNativePtr = nCreateFamily(); + mNativePtr = nCreateFamily(); + if (mNativePtr == 0) { + throw new RuntimeException(); + } + } + + @Override + protected void finalize() throws Throwable { + try { + nUnrefFamily(mNativePtr); + } finally { + super.finalize(); + } + } + + public boolean addFont(File path) { + return nAddFont(mNativePtr, path.getAbsolutePath()); + } + + static native long nCreateFamily(); + static native void nUnrefFamily(long nativePtr); + static native boolean nAddFont(long nativeFamily, String path); +} diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java new file mode 100644 index 0000000..f304f4e --- /dev/null +++ b/graphics/java/android/graphics/FontListParser.java @@ -0,0 +1,117 @@ +/* + * 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; + +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * Parser for font config files. + * + * @hide + */ +public class FontListParser { + + public static class Family { + public Family(List<String> names, List<String> fontFiles) { + this.names = names; + this.fontFiles = fontFiles; + } + + public List<String> names; + // todo: need attributes for font files + public List<String> fontFiles; + } + + /* Parse fallback list (no names) */ + public static List<Family> parse(InputStream in) throws XmlPullParserException, IOException { + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilies(parser); + } finally { + in.close(); + } + } + + private static List<Family> readFamilies(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<Family> families = new ArrayList<Family>(); + parser.require(XmlPullParser.START_TAG, null, "familyset"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + if (parser.getName().equals("family")) { + families.add(readFamily(parser)); + } else { + skip(parser); + } + } + return families; + } + + private static Family readFamily(XmlPullParser parser) + throws XmlPullParserException, IOException { + List<String> names = null; + List<String> fontFiles = new ArrayList<String>(); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + String tag = parser.getName(); + if (tag.equals("fileset")) { + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + if (parser.getName().equals("file")) { + String filename = parser.nextText(); + String fullFilename = "/system/fonts/" + filename; + fontFiles.add(fullFilename); + } + } + } else if (tag.equals("nameset")) { + names = new ArrayList<String>(); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + if (parser.getName().equals("name")) { + String name = parser.nextText(); + names.add(name); + } + } + } + } + return new Family(names, fontFiles); + } + + private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + int depth = 1; + while (depth > 0) { + switch (parser.next()) { + case XmlPullParser.START_TAG: + depth++; + break; + case XmlPullParser.END_TAG: + depth--; + break; + } + } + } +} diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index 062acaf..fe53a17 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -272,6 +272,7 @@ public class ImageFormat { case NV16: case YUY2: case YV12: + case JPEG: case NV21: case YUV_420_888: case RAW_SENSOR: diff --git a/graphics/java/android/graphics/LayerRasterizer.java b/graphics/java/android/graphics/LayerRasterizer.java index 5b35608..e7a24a4 100644 --- a/graphics/java/android/graphics/LayerRasterizer.java +++ b/graphics/java/android/graphics/LayerRasterizer.java @@ -16,11 +16,12 @@ package android.graphics; +@Deprecated public class LayerRasterizer extends Rasterizer { public LayerRasterizer() { native_instance = nativeConstructor(); } - + /** Add a new layer (above any previous layers) to the rasterizer. The layer will extract those fields that affect the mask from the specified paint, but will not retain a reference to the paint diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java index 66bf75c..b4e6bab 100644 --- a/graphics/java/android/graphics/Matrix.java +++ b/graphics/java/android/graphics/Matrix.java @@ -245,6 +245,16 @@ public class Matrix { } /** + * Gets whether this matrix is affine. An affine matrix preserves + * straight lines and has no perspective. + * + * @return Whether the matrix is affine. + */ + public boolean isAffine() { + return native_isAffine(native_instance); + } + + /** * Returns true if will map a rectangle to another rectangle. This can be * true if the matrix is identity, scale-only, or rotates a multiple of 90 * degrees. @@ -828,6 +838,7 @@ public class Matrix { private static native long native_create(long native_src_or_zero); private static native boolean native_isIdentity(long native_object); + private static native boolean native_isAffine(long native_object); private static native boolean native_rectStaysRect(long native_object); private static native void native_reset(long native_object); private static native void native_set(long native_object, diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java index b5c0801..c6ba75c 100644 --- a/graphics/java/android/graphics/Outline.java +++ b/graphics/java/android/graphics/Outline.java @@ -53,8 +53,7 @@ public final class Outline { set(src); } - /** @hide */ - public void markInvalid() { + public void reset() { mRadius = 0; mRect = null; mPath = null; @@ -74,27 +73,21 @@ public final class Outline { * * @param src Source outline to copy from. */ - public void set(@Nullable Outline src) { - if (src == null) { - mRadius = 0; - mRect = null; - mPath = null; - } else { - if (src.mPath != null) { - if (mPath == null) { - mPath = new Path(); - } - mPath.set(src.mPath); - mRect = null; + public void set(@NonNull Outline src) { + if (src.mPath != null) { + if (mPath == null) { + mPath = new Path(); } - if (src.mRect != null) { - if (mRect == null) { - mRect = new Rect(); - } - mRect.set(src.mRect); + mPath.set(src.mPath); + mRect = null; + } + if (src.mRect != null) { + if (mRect == null) { + mRect = new Rect(); } - mRadius = src.mRadius; + mRect.set(src.mRect); } + mRadius = src.mRadius; } /** @@ -134,6 +127,11 @@ public final class Outline { * Sets the outline to the oval defined by input rect. */ public void setOval(int left, int top, int right, int bottom) { + if ((bottom - top) == (right - left)) { + // represent circle as round rect, for efficiency, and to enable clipping + setRoundRect(left, top, right, bottom, (bottom - top) / 2.0f); + return; + } mRect = null; if (mPath == null) mPath = new Path(); mPath.reset(); @@ -160,4 +158,16 @@ public final class Outline { mRadius = -1.0f; mPath.set(convexPath); } + + /** + * Returns whether the outline can be used to clip a View. + * + * Currently, only outlines that can be represented as a rectangle, circle, or round rect + * support clipping. + * + * @see {@link View#setClipToOutline(boolean)} + */ + public boolean canClip() { + return mRect != null; + } } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 457b3ea..92cfd6b 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1064,14 +1064,17 @@ public class Paint { mNativeTypeface = typefaceNative; return typeface; } - + /** * Get the paint's rasterizer (or null). * <p /> * The raster controls/modifies how paths/text are turned into alpha masks. * * @return the paint's rasterizer (or null) + * + * @deprecated Rasterizer is not supported by either the HW or PDF backends. */ + @Deprecated public Rasterizer getRasterizer() { return mRasterizer; } @@ -1085,7 +1088,10 @@ public class Paint { * @param rasterizer May be null. The new rasterizer to be installed in * the paint. * @return rasterizer + * + * @deprecated Rasterizer is not supported by either the HW or PDF backends. */ + @Deprecated public Rasterizer setRasterizer(Rasterizer rasterizer) { long rasterizerNative = 0; if (rasterizer != null) { @@ -1095,7 +1101,7 @@ public class Paint { mRasterizer = rasterizer; return rasterizer; } - + /** * This draws a shadow layer below the main layer, with the specified * offset and color, and blur radius. If radius is 0, then the shadow @@ -1655,12 +1661,12 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - return native_getTextWidths(mNativePaint, text, index, count, mBidiFlags, widths); + return native_getTextWidths(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, widths); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - int res = native_getTextWidths(mNativePaint, text, index, count, mBidiFlags, widths); + int res = native_getTextWidths(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags, widths); setTextSize(oldSize); for (int i=0; i<res; i++) { widths[i] *= mInvCompatScaling; @@ -1737,12 +1743,12 @@ public class Paint { return 0; } if (!mHasCompatScaling) { - return native_getTextWidths(mNativePaint, text, start, end, mBidiFlags, widths); + return native_getTextWidths(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, widths); } final float oldSize = getTextSize(); setTextSize(oldSize*mCompatScaling); - int res = native_getTextWidths(mNativePaint, text, start, end, mBidiFlags, widths); + int res = native_getTextWidths(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, widths); setTextSize(oldSize); for (int i=0; i<res; i++) { widths[i] *= mInvCompatScaling; @@ -1832,13 +1838,13 @@ public class Paint { return 0f; } if (!mHasCompatScaling) { - return native_getTextRunAdvances(mNativePaint, chars, index, count, + return native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count, contextIndex, contextCount, flags, advances, advancesIndex); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - float res = native_getTextRunAdvances(mNativePaint, chars, index, count, + float res = native_getTextRunAdvances(mNativePaint, mNativeTypeface, chars, index, count, contextIndex, contextCount, flags, advances, advancesIndex); setTextSize(oldSize); @@ -1963,13 +1969,13 @@ public class Paint { } if (!mHasCompatScaling) { - return native_getTextRunAdvances(mNativePaint, text, start, end, + return native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end, contextStart, contextEnd, flags, advances, advancesIndex); } final float oldSize = getTextSize(); setTextSize(oldSize * mCompatScaling); - float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end, + float totalAdvance = native_getTextRunAdvances(mNativePaint, mNativeTypeface, text, start, end, contextStart, contextEnd, flags, advances, advancesIndex); setTextSize(oldSize); @@ -2234,19 +2240,19 @@ public class Paint { private static native void native_setTextLocale(long native_object, String locale); - private static native int native_getTextWidths(long native_object, + private static native int native_getTextWidths(long native_object, long native_typeface, char[] text, int index, int count, int bidiFlags, float[] widths); - private static native int native_getTextWidths(long native_object, + private static native int native_getTextWidths(long native_object, long native_typeface, String text, int start, int end, int bidiFlags, float[] widths); private static native int native_getTextGlyphs(long native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, char[] glyphs); - private static native float native_getTextRunAdvances(long native_object, + private static native float native_getTextRunAdvances(long native_object, long native_typeface, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex); - private static native float native_getTextRunAdvances(long native_object, + private static native float native_getTextRunAdvances(long native_object, long native_typeface, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex); diff --git a/graphics/java/android/graphics/Rasterizer.java b/graphics/java/android/graphics/Rasterizer.java index 817814c..c351d94e 100644 --- a/graphics/java/android/graphics/Rasterizer.java +++ b/graphics/java/android/graphics/Rasterizer.java @@ -21,6 +21,7 @@ package android.graphics; +@Deprecated public class Rasterizer { protected void finalize() throws Throwable { diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 8b5609f..437d2f4 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -36,9 +36,21 @@ public final class Rect implements Parcelable { public int right; public int bottom; - private static final Pattern FLATTENED_PATTERN = Pattern.compile( + /** + * A helper class for flattened rectange pattern recognition. A separate + * class to avoid an initialization dependency on a regular expression + * causing Rect to not be initializable with an ahead-of-time compilation + * scheme. + */ + private static final class UnflattenHelper { + private static final Pattern FLATTENED_PATTERN = Pattern.compile( "(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)"); + static Matcher getMatcher(String str) { + return FLATTENED_PATTERN.matcher(str); + } + } + /** * Create a new empty Rect. All coordinates are initialized to 0. */ @@ -152,7 +164,7 @@ public final class Rect implements Parcelable { * or null if the string is not of that form. */ public static Rect unflattenFromString(String str) { - Matcher matcher = FLATTENED_PATTERN.matcher(str); + Matcher matcher = UnflattenHelper.getMatcher(str); if (!matcher.matches()) { return null; } diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 73e0e8d..64451c4 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -17,10 +17,21 @@ package android.graphics; import android.content.res.AssetManager; -import android.util.SparseArray; +import android.graphics.FontListParser.Family; +import android.util.Log; import android.util.LongSparseArray; +import android.util.SparseArray; + +import org.xmlpull.v1.XmlPullParserException; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * The Typeface class specifies the typeface and intrinsic style of a font. @@ -30,6 +41,8 @@ import java.io.File; */ public class Typeface { + private static String TAG = "Typeface"; + /** The default NORMAL typeface object */ public static final Typeface DEFAULT; /** @@ -49,6 +62,10 @@ public class Typeface { private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache = new LongSparseArray<SparseArray<Typeface>>(3); + static Typeface sDefaultTypeface; + static Map<String, Typeface> sSystemFontMap; + static FontFamily[] sFallbackFonts; + /** * @hide */ @@ -62,6 +79,11 @@ public class Typeface { private int mStyle = 0; + private static void setDefault(Typeface t) { + sDefaultTypeface = t; + nativeSetDefault(t.native_instance); + } + /** Returns the typeface's intrinsic style attributes */ public int getStyle() { return mStyle; @@ -89,6 +111,9 @@ public class Typeface { * @return The best matching typeface. */ public static Typeface create(String familyName, int style) { + if (sSystemFontMap != null) { + return create(sSystemFontMap.get(familyName), style); + } return new Typeface(nativeCreate(familyName, style)); } @@ -142,7 +167,7 @@ public class Typeface { public static Typeface defaultFromStyle(int style) { return sDefaults[style]; } - + /** * Create a new typeface from the specified font data. * @param mgr The application's asset manager @@ -156,7 +181,7 @@ public class Typeface { /** * Create a new typeface from the specified font file. * - * @param path The path to the font data. + * @param path The path to the font data. * @return The new typeface. */ public static Typeface createFromFile(File path) { @@ -166,13 +191,45 @@ public class Typeface { /** * Create a new typeface from the specified font file. * - * @param path The full path to the font data. + * @param path The full path to the font data. * @return The new typeface. */ public static Typeface createFromFile(String path) { return new Typeface(nativeCreateFromFile(path)); } + /** + * Create a new typeface from an array of font families. + * + * @param families array of font families + * @hide + */ + public static Typeface createFromFamilies(FontFamily[] families) { + long[] ptrArray = new long[families.length]; + for (int i = 0; i < families.length; i++) { + ptrArray[i] = families[i].mNativePtr; + } + return new Typeface(nativeCreateFromArray(ptrArray)); + } + + /** + * Create a new typeface from an array of font families, including + * also the font families in the fallback list. + * + * @param families array of font families + * @hide + */ + public static Typeface createFromFamiliesWithDefault(FontFamily[] families) { + long[] ptrArray = new long[families.length + sFallbackFonts.length]; + for (int i = 0; i < families.length; i++) { + ptrArray[i] = families[i].mNativePtr; + } + for (int i = 0; i < sFallbackFonts.length; i++) { + ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr; + } + return new Typeface(nativeCreateFromArray(ptrArray)); + } + // don't allow clients to call this directly private Typeface(long ni) { if (ni == 0) { @@ -182,14 +239,76 @@ public class Typeface { native_instance = ni; mStyle = nativeGetStyle(ni); } - + + private static FontFamily makeFamilyFromParsed(FontListParser.Family family) { + // TODO: expand to handle attributes like lang and variant + FontFamily fontFamily = new FontFamily(); + for (String fontFile : family.fontFiles) { + fontFamily.addFont(new File(fontFile)); + } + return fontFamily; + } + static { + // Load font config and initialize Minikin state + String systemConfigFilename = "/system/etc/system_fonts.xml"; + String configFilename = "/system/etc/fallback_fonts.xml"; + try { + // TODO: throws an exception non-Minikin builds, to fail early; + // remove when Minikin-only + new FontFamily(); + + FileInputStream systemIn = new FileInputStream(systemConfigFilename); + List<FontListParser.Family> systemFontConfig = FontListParser.parse(systemIn); + + FileInputStream fallbackIn = new FileInputStream(configFilename); + List<FontFamily> familyList = new ArrayList<FontFamily>(); + // Note that the default typeface is always present in the fallback list; + // this is an enhancement from pre-Minikin behavior. + familyList.add(makeFamilyFromParsed(systemFontConfig.get(0))); + for (Family f : FontListParser.parse(fallbackIn)) { + familyList.add(makeFamilyFromParsed(f)); + } + sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]); + setDefault(Typeface.createFromFamilies(sFallbackFonts)); + + Map<String, Typeface> systemFonts = new HashMap<String, Typeface>(); + for (int i = 0; i < systemFontConfig.size(); i++) { + Typeface typeface; + Family f = systemFontConfig.get(i); + if (i == 0) { + // The first entry is the default typeface; no sense in duplicating + // the corresponding FontFamily. + typeface = sDefaultTypeface; + } else { + FontFamily fontFamily = makeFamilyFromParsed(f); + FontFamily[] families = { fontFamily }; + typeface = Typeface.createFromFamiliesWithDefault(families); + } + for (String name : f.names) { + systemFonts.put(name, typeface); + } + } + sSystemFontMap = systemFonts; + + } catch (RuntimeException e) { + Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)"); + // TODO: normal in non-Minikin case, remove or make error when Minikin-only + } catch (FileNotFoundException e) { + Log.e(TAG, "Error opening " + configFilename); + } catch (IOException e) { + Log.e(TAG, "Error reading " + configFilename); + } catch (XmlPullParserException e) { + Log.e(TAG, "XML parse exception for " + configFilename); + } + + // Set up defaults and typefaces exposed in public API DEFAULT = create((String) null, 0); DEFAULT_BOLD = create((String) null, Typeface.BOLD); SANS_SERIF = create("sans-serif", 0); SERIF = create("serif", 0); MONOSPACE = create("monospace", 0); - + sDefaults = new Typeface[] { DEFAULT, DEFAULT_BOLD, @@ -198,6 +317,7 @@ public class Typeface { }; } + @Override protected void finalize() throws Throwable { try { nativeUnref(native_instance); @@ -234,4 +354,6 @@ public class Typeface { private static native int nativeGetStyle(long native_instance); private static native long nativeCreateFromAsset(AssetManager mgr, String path); private static native long nativeCreateFromFile(String path); + private static native long nativeCreateFromArray(long[] familyArray); + private static native void nativeSetDefault(long native_instance); } diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java index 60b4615..6755f3e 100644 --- a/graphics/java/android/graphics/drawable/BitmapDrawable.java +++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java @@ -67,15 +67,22 @@ import java.io.IOException; * @attr ref android.R.styleable#BitmapDrawable_tileMode */ public class BitmapDrawable extends Drawable { - private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG; + + // Constants for {@link android.R.styleable#BitmapDrawable_tileMode}. + private static final int TILE_MODE_UNDEFINED = -2; + private static final int TILE_MODE_DISABLED = -1; + private static final int TILE_MODE_CLAMP = 0; + private static final int TILE_MODE_REPEAT = 1; + private static final int TILE_MODE_MIRROR = 2; + + private final Rect mDstRect = new Rect(); // Gravity.apply() sets this + private BitmapState mBitmapState; - private Bitmap mBitmap; private PorterDuffColorFilter mTintFilter; - private int mTargetDensity; - private final Rect mDstRect = new Rect(); // Gravity.apply() sets this + private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; private boolean mApplyGravity; private boolean mMutated; @@ -100,11 +107,12 @@ public class BitmapDrawable extends Drawable { /** * Create an empty drawable, setting initial target density based on * the display metrics of the resources. + * * @deprecated Use {@link #BitmapDrawable(android.content.res.Resources, android.graphics.Bitmap)} * instead to specify a bitmap to draw with. */ + @SuppressWarnings("unused") @Deprecated - @SuppressWarnings({"UnusedParameters"}) public BitmapDrawable(Resources res) { mBitmapState = new BitmapState((Bitmap) null); mBitmapState.mTargetDensity = mTargetDensity; @@ -137,7 +145,7 @@ public class BitmapDrawable extends Drawable { @Deprecated public BitmapDrawable(String filepath) { this(new BitmapState(BitmapFactory.decodeFile(filepath)), null, null); - if (mBitmap == null) { + if (mBitmapState.mBitmap == null) { android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); } } @@ -145,11 +153,11 @@ public class BitmapDrawable extends Drawable { /** * Create a drawable by opening a given file path and decoding the bitmap. */ - @SuppressWarnings({"UnusedParameters"}) + @SuppressWarnings("unused") public BitmapDrawable(Resources res, String filepath) { this(new BitmapState(BitmapFactory.decodeFile(filepath)), null, null); mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmap == null) { + if (mBitmapState.mBitmap == null) { android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath); } } @@ -162,7 +170,7 @@ public class BitmapDrawable extends Drawable { @Deprecated public BitmapDrawable(java.io.InputStream is) { this(new BitmapState(BitmapFactory.decodeStream(is)), null, null); - if (mBitmap == null) { + if (mBitmapState.mBitmap == null) { android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); } } @@ -170,11 +178,11 @@ public class BitmapDrawable extends Drawable { /** * Create a drawable by decoding a bitmap from the given input stream. */ - @SuppressWarnings({"UnusedParameters"}) + @SuppressWarnings("unused") public BitmapDrawable(Resources res, java.io.InputStream is) { this(new BitmapState(BitmapFactory.decodeStream(is)), null, null); mBitmapState.mTargetDensity = mTargetDensity; - if (mBitmap == null) { + if (mBitmapState.mBitmap == null) { android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is); } } @@ -190,22 +198,23 @@ public class BitmapDrawable extends Drawable { * Returns the bitmap used by this drawable to render. May be null. */ public final Bitmap getBitmap() { - return mBitmap; + return mBitmapState.mBitmap; } private void computeBitmapSize() { - mBitmapWidth = mBitmap.getScaledWidth(mTargetDensity); - mBitmapHeight = mBitmap.getScaledHeight(mTargetDensity); + final Bitmap bitmap = mBitmapState.mBitmap; + if (bitmap != null) { + mBitmapWidth = bitmap.getScaledWidth(mTargetDensity); + mBitmapHeight = bitmap.getScaledHeight(mTargetDensity); + } else { + mBitmapWidth = mBitmapHeight = -1; + } } private void setBitmap(Bitmap bitmap) { - if (bitmap != mBitmap) { - mBitmap = bitmap; - if (bitmap != null) { - computeBitmapSize(); - } else { - mBitmapWidth = mBitmapHeight = -1; - } + if (mBitmapState.mBitmap != bitmap) { + mBitmapState.mBitmap = bitmap; + computeBitmapSize(); invalidateSelf(); } } @@ -247,7 +256,7 @@ public class BitmapDrawable extends Drawable { public void setTargetDensity(int density) { if (mTargetDensity != density) { mTargetDensity = density == 0 ? DisplayMetrics.DENSITY_DEFAULT : density; - if (mBitmap != null) { + if (mBitmapState.mBitmap != null) { computeBitmapSize(); } invalidateSelf(); @@ -343,8 +352,9 @@ public class BitmapDrawable extends Drawable { /** * Indicates the repeat behavior of this drawable on the X axis. * - * @return {@link Shader.TileMode#CLAMP} if the bitmap does not repeat, - * {@link Shader.TileMode#REPEAT} or {@link Shader.TileMode#MIRROR} otherwise. + * @return {@link android.graphics.Shader.TileMode#CLAMP} if the bitmap does not repeat, + * {@link android.graphics.Shader.TileMode#REPEAT} or + * {@link android.graphics.Shader.TileMode#MIRROR} otherwise. */ public Shader.TileMode getTileModeX() { return mBitmapState.mTileModeX; @@ -353,8 +363,9 @@ public class BitmapDrawable extends Drawable { /** * Indicates the repeat behavior of this drawable on the Y axis. * - * @return {@link Shader.TileMode#CLAMP} if the bitmap does not repeat, - * {@link Shader.TileMode#REPEAT} or {@link Shader.TileMode#MIRROR} otherwise. + * @return {@link android.graphics.Shader.TileMode#CLAMP} if the bitmap does not repeat, + * {@link android.graphics.Shader.TileMode#REPEAT} or + * {@link android.graphics.Shader.TileMode#MIRROR} otherwise. */ public Shader.TileMode getTileModeY() { return mBitmapState.mTileModeY; @@ -362,9 +373,9 @@ public class BitmapDrawable extends Drawable { /** * Sets the repeat behavior of this drawable on the X axis. By default, the drawable - * does not repeat its bitmap. Using {@link Shader.TileMode#REPEAT} or - * {@link Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) if the bitmap - * is smaller than this drawable. + * does not repeat its bitmap. Using {@link android.graphics.Shader.TileMode#REPEAT} or + * {@link android.graphics.Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) + * if the bitmap is smaller than this drawable. * * @param mode The repeat mode for this drawable. * @@ -377,9 +388,9 @@ public class BitmapDrawable extends Drawable { /** * Sets the repeat behavior of this drawable on the Y axis. By default, the drawable - * does not repeat its bitmap. Using {@link Shader.TileMode#REPEAT} or - * {@link Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) if the bitmap - * is smaller than this drawable. + * does not repeat its bitmap. Using {@link android.graphics.Shader.TileMode#REPEAT} or + * {@link android.graphics.Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) + * if the bitmap is smaller than this drawable. * * @param mode The repeat mode for this drawable. * @@ -392,9 +403,9 @@ public class BitmapDrawable extends Drawable { /** * Sets the repeat behavior of this drawable on both axis. By default, the drawable - * does not repeat its bitmap. Using {@link Shader.TileMode#REPEAT} or - * {@link Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) if the bitmap - * is smaller than this drawable. + * does not repeat its bitmap. Using {@link android.graphics.Shader.TileMode#REPEAT} or + * {@link android.graphics.Shader.TileMode#MIRROR} the bitmap can be repeated (or tiled) + * if the bitmap is smaller than this drawable. * * @param xmode The X repeat mode for this drawable. * @param ymode The Y repeat mode for this drawable. @@ -462,7 +473,7 @@ public class BitmapDrawable extends Drawable { @Override public void draw(Canvas canvas) { - final Bitmap bitmap = mBitmap; + final Bitmap bitmap = mBitmapState.mBitmap; if (bitmap == null) { return; } @@ -589,7 +600,7 @@ public class BitmapDrawable extends Drawable { public void setTint(ColorStateList tint) { if (mBitmapState.mTint != tint) { mBitmapState.mTint = tint; - updateTintFilter(); + computeTintFilter(); invalidateSelf(); } } @@ -612,7 +623,7 @@ public class BitmapDrawable extends Drawable { public void setTintMode(Mode tintMode) { if (mBitmapState.mTintMode != tintMode) { mBitmapState.mTintMode = tintMode; - updateTintFilter(); + computeTintFilter(); invalidateSelf(); } } @@ -624,18 +635,15 @@ public class BitmapDrawable extends Drawable { return mBitmapState.mTintMode; } - /** - * Ensures the tint filter is consistent with the current tint color and - * mode. - */ - private void updateTintFilter() { - final ColorStateList tint = mBitmapState.mTint; - final Mode tintMode = mBitmapState.mTintMode; - if (tint != null && tintMode != null) { - if (mTintFilter == null) { - mTintFilter = new PorterDuffColorFilter(0, tintMode); + private void computeTintFilter() { + final BitmapState state = mBitmapState; + if (state.mTint != null && state.mTintMode != null) { + final int color = state.mTint.getColorForState(getState(), 0); + if (mTintFilter != null) { + mTintFilter.setColor(color); + mTintFilter.setMode(state.mTintMode); } else { - mTintFilter.setMode(tintMode); + mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); } } else { mTintFilter = null; @@ -693,16 +701,15 @@ public class BitmapDrawable extends Drawable { throws XmlPullParserException, IOException { super.inflate(r, parser, attrs, theme); - final TypedArray a = obtainAttributes( - r, theme, attrs, R.styleable.BitmapDrawable); - inflateStateFromTypedArray(a); + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.BitmapDrawable); + updateStateFromTypedArray(a); a.recycle(); } /** - * Initializes the constant state from the values in the typed array. + * Updates the constant state from the values in the typed array. */ - private void inflateStateFromTypedArray(TypedArray a) throws XmlPullParserException { + private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException { final Resources r = a.getResources(); final BitmapState state = mBitmapState; @@ -710,86 +717,52 @@ public class BitmapDrawable extends Drawable { final int[] themeAttrs = a.extractThemeAttrs(); state.mThemeAttrs = themeAttrs; - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_src] == 0) { - final int id = a.getResourceId(R.styleable.BitmapDrawable_src, 0); - if (id == 0) { - throw new XmlPullParserException(a.getPositionDescription() + - ": <bitmap> requires a valid src attribute"); - } - - final Bitmap bitmap = BitmapFactory.decodeResource(r, id); + final int srcResId = a.getResourceId(R.styleable.BitmapDrawable_src, 0); + if (srcResId != 0) { + final Bitmap bitmap = BitmapFactory.decodeResource(r, srcResId); if (bitmap == null) { throw new XmlPullParserException(a.getPositionDescription() + ": <bitmap> requires a valid src attribute"); } + state.mBitmap = bitmap; - setBitmap(bitmap); } - setTargetDensity(r.getDisplayMetrics()); + state.mTargetDensity = r.getDisplayMetrics().densityDpi; - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_mipMap] == 0) { - final boolean defMipMap = state.mBitmap != null ? state.mBitmap.hasMipMap() : false; - final boolean mipMap = a.getBoolean( - R.styleable.BitmapDrawable_mipMap, defMipMap); - setMipMap(mipMap); - } + final boolean defMipMap = state.mBitmap != null ? state.mBitmap.hasMipMap() : false; + setMipMap(a.getBoolean(R.styleable.BitmapDrawable_mipMap, defMipMap)); - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_autoMirrored] == 0) { - final boolean autoMirrored = a.getBoolean( - R.styleable.BitmapDrawable_autoMirrored, false); - setAutoMirrored(autoMirrored); - } + state.mAutoMirrored = a.getBoolean( + R.styleable.BitmapDrawable_autoMirrored, state.mAutoMirrored); + state.mBaseAlpha = a.getFloat(R.styleable.BitmapDrawable_alpha, state.mBaseAlpha); - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_tintMode] == 0) { - final int tintModeValue = a.getInt( - R.styleable.BitmapDrawable_tintMode, -1); - state.mTintMode = Drawable.parseTintMode(tintModeValue, Mode.SRC_IN); + final int tintMode = a.getInt(R.styleable.BitmapDrawable_tintMode, -1); + if (tintMode != -1) { + state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN); } - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_tint] == 0) { - state.mTint = a.getColorStateList(R.styleable.BitmapDrawable_tint); - if (state.mTint != null) { - final int color = state.mTint.getColorForState(getState(), 0); - mTintFilter = new PorterDuffColorFilter(color, mBitmapState.mTintMode); - } + final ColorStateList tint = a.getColorStateList(R.styleable.BitmapDrawable_tint); + if (tint != null) { + state.mTint = tint; } final Paint paint = mBitmapState.mPaint; + paint.setAntiAlias(a.getBoolean( + R.styleable.BitmapDrawable_antialias, paint.isAntiAlias())); + paint.setFilterBitmap(a.getBoolean( + R.styleable.BitmapDrawable_filter, paint.isFilterBitmap())); + paint.setDither(a.getBoolean(R.styleable.BitmapDrawable_dither, paint.isDither())); - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_antialias] == 0) { - final boolean antiAlias = a.getBoolean( - R.styleable.BitmapDrawable_antialias, paint.isAntiAlias()); - paint.setAntiAlias(antiAlias); - } - - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_filter] == 0) { - final boolean filter = a.getBoolean( - R.styleable.BitmapDrawable_filter, paint.isFilterBitmap()); - paint.setFilterBitmap(filter); - } + setGravity(a.getInt(R.styleable.BitmapDrawable_gravity, state.mGravity)); - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_dither] == 0) { - final boolean dither = a.getBoolean( - R.styleable.BitmapDrawable_dither, paint.isDither()); - paint.setDither(dither); - } - - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_alpha] == 0) { - state.mBaseAlpha = a.getFloat(R.styleable.BitmapDrawable_alpha, 1.0f); - } - - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_gravity] == 0) { - final int gravity = a.getInt( - R.styleable.BitmapDrawable_gravity, Gravity.FILL); - setGravity(gravity); - } - - if (themeAttrs == null || themeAttrs[R.styleable.BitmapDrawable_tileMode] == 0) { - final int tileMode = a.getInt( - R.styleable.BitmapDrawable_tileMode, -1); + final int tileMode = a.getInt(R.styleable.BitmapDrawable_tileMode, TILE_MODE_UNDEFINED); + if (tileMode != TILE_MODE_UNDEFINED) { setTileModeInternal(tileMode); } + + // Update local properties. + initializeWithState(state, r); } @Override @@ -797,120 +770,32 @@ public class BitmapDrawable extends Drawable { super.applyTheme(t); final BitmapState state = mBitmapState; - if (state == null) { - throw new RuntimeException("Can't apply theme to <bitmap> with no constant state"); + if (state == null || state.mThemeAttrs == null) { + return; } - final int[] themeAttrs = state.mThemeAttrs; - if (themeAttrs != null) { - final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.BitmapDrawable, 0, 0); + final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.BitmapDrawable); + try { updateStateFromTypedArray(a); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } finally { a.recycle(); } } - /** - * Updates the constant state from the values in the typed array. - */ - private void updateStateFromTypedArray(TypedArray a) { - final Resources r = a.getResources(); - final BitmapState state = mBitmapState; - final Paint paint = mBitmapState.mPaint; - - if (a.hasValue(R.styleable.BitmapDrawable_antialias)) { - final boolean antiAlias = a.getBoolean( - R.styleable.BitmapDrawable_antialias, paint.isAntiAlias()); - paint.setAntiAlias(antiAlias); - } - - if (a.hasValue(R.styleable.BitmapDrawable_filter)) { - final boolean filter = a.getBoolean( - R.styleable.BitmapDrawable_filter, paint.isFilterBitmap()); - paint.setFilterBitmap(filter); - } - - if (a.hasValue(R.styleable.BitmapDrawable_dither)) { - final boolean dither = a.getBoolean( - R.styleable.BitmapDrawable_dither, paint.isDither()); - paint.setDither(dither); - } - - if (a.hasValue(R.styleable.BitmapDrawable_alpha)) { - state.mBaseAlpha = a.getFloat(R.styleable.BitmapDrawable_alpha, state.mBaseAlpha); - } - - if (a.hasValue(R.styleable.BitmapDrawable_gravity)) { - final int gravity = a.getInt( - R.styleable.BitmapDrawable_gravity, Gravity.FILL); - setGravity(gravity); - } - - if (a.hasValue(R.styleable.BitmapDrawable_tileMode)) { - final int tileMode = a.getInt( - R.styleable.BitmapDrawable_tileMode, -1); - setTileModeInternal(tileMode); - } - - if (a.hasValue(R.styleable.BitmapDrawable_src)) { - final int id = a.getResourceId(R.styleable.BitmapDrawable_src, 0); - if (id == 0) { - throw new RuntimeException(a.getPositionDescription() + - ": <bitmap> requires a valid src attribute"); - } - - final Bitmap bitmap = BitmapFactory.decodeResource(r, id); - if (bitmap == null) { - throw new RuntimeException(a.getPositionDescription() + - ": <bitmap> requires a valid src attribute"); - } - - setBitmap(bitmap); - } - - setTargetDensity(r.getDisplayMetrics()); - - if (a.hasValue(R.styleable.BitmapDrawable_mipMap)) { - final boolean mipMap = a.getBoolean( - R.styleable.BitmapDrawable_mipMap, - state.mBitmap.hasMipMap()); - setMipMap(mipMap); - } - - if (a.hasValue(R.styleable.BitmapDrawable_autoMirrored)) { - final boolean autoMirrored = a.getBoolean( - R.styleable.BitmapDrawable_autoMirrored, false); - setAutoMirrored(autoMirrored); - } - - if (a.hasValue(R.styleable.BitmapDrawable_tintMode)) { - final int modeValue = a.getInt( - R.styleable.BitmapDrawable_tintMode, -1); - state.mTintMode = Drawable.parseTintMode(modeValue, Mode.SRC_IN); - } - - if (a.hasValue(R.styleable.BitmapDrawable_tint)) { - final ColorStateList tint = a.getColorStateList( - R.styleable.BitmapDrawable_tint); - if (tint != null) { - state.mTint = tint; - final int color = tint.getColorForState(getState(), 0); - mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); - } - } - } - private void setTileModeInternal(final int tileMode) { switch (tileMode) { - case -1: - // Do nothing. + case TILE_MODE_DISABLED: + setTileModeXY(null, null); break; - case 0: + case TILE_MODE_CLAMP: setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); break; - case 1: + case TILE_MODE_REPEAT: setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); break; - case 2: + case TILE_MODE_MIRROR: setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR); break; } @@ -936,8 +821,9 @@ public class BitmapDrawable extends Drawable { if (mBitmapState.mGravity != Gravity.FILL) { return PixelFormat.TRANSLUCENT; } - Bitmap bm = mBitmap; - return (bm == null || bm.hasAlpha() || mBitmapState.mPaint.getAlpha() < 255) ? + + final Bitmap bitmap = mBitmapState.mBitmap; + return (bitmap == null || bitmap.hasAlpha() || mBitmapState.mPaint.getAlpha() < 255) ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; } @@ -948,22 +834,26 @@ public class BitmapDrawable extends Drawable { } final static class BitmapState extends ConstantState { - Bitmap mBitmap; - ColorStateList mTint; + final Paint mPaint; + + // Values loaded during inflation. + int[] mThemeAttrs = null; + Bitmap mBitmap = null; + ColorStateList mTint = null; Mode mTintMode = Mode.SRC_IN; - int[] mThemeAttrs; - int mChangingConfigurations; int mGravity = Gravity.FILL; float mBaseAlpha = 1.0f; - Paint mPaint = new Paint(DEFAULT_PAINT_FLAGS); Shader.TileMode mTileModeX = null; Shader.TileMode mTileModeY = null; int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; + boolean mAutoMirrored = false; + + int mChangingConfigurations; boolean mRebuildShader; - boolean mAutoMirrored; BitmapState(Bitmap bitmap) { mBitmap = bitmap; + mPaint = new Paint(DEFAULT_PAINT_FLAGS); } BitmapState(BitmapState bitmapState) { @@ -1013,6 +903,10 @@ public class BitmapDrawable extends Drawable { } } + /** + * The one constructor to rule them all. This is called by all public + * constructors to set the state and initialize local properties. + */ private BitmapDrawable(BitmapState state, Resources res, Theme theme) { if (theme != null && state.canApplyTheme()) { mBitmapState = new BitmapState(state); @@ -1034,11 +928,7 @@ public class BitmapDrawable extends Drawable { mTargetDensity = state.mTargetDensity; } - if (state.mTint != null) { - final int color = state.mTint.getColorForState(getState(), 0); - mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); - } - - setBitmap(state.mBitmap); + computeTintFilter(); + computeBitmapSize(); } } diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java index 8243b7c..df5ca33 100644 --- a/graphics/java/android/graphics/drawable/ColorDrawable.java +++ b/graphics/java/android/graphics/drawable/ColorDrawable.java @@ -199,7 +199,7 @@ public class ColorDrawable extends Drawable { final int[] themeAttrs = state.mThemeAttrs; if (themeAttrs != null) { - final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.ColorDrawable, 0, 0); + final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.ColorDrawable); updateStateFromTypedArray(a); a.recycle(); } diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index dc06350..8fe1544 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -1053,7 +1053,7 @@ public class GradientDrawable extends Drawable { final int[] themeAttrs = state.mThemeAttrs; if (themeAttrs != null) { final TypedArray a = t.resolveAttributes( - themeAttrs, R.styleable.GradientDrawable, 0, 0); + themeAttrs, R.styleable.GradientDrawable); updateStateFromTypedArray(a); a.recycle(); @@ -1123,37 +1123,37 @@ public class GradientDrawable extends Drawable { TypedArray a; if (state.mAttrSize != null) { - a = t.resolveAttributes(state.mAttrSize, R.styleable.GradientDrawableSize, 0, 0); + a = t.resolveAttributes(state.mAttrSize, R.styleable.GradientDrawableSize); // TODO: updateGradientDrawableSize(a); a.recycle(); } if (state.mAttrGradient != null) { - a = t.resolveAttributes(state.mAttrGradient, R.styleable.GradientDrawableGradient, 0, 0); + a = t.resolveAttributes(state.mAttrGradient, R.styleable.GradientDrawableGradient); // TODO: updateGradientDrawableGradient(a); a.recycle(); } if (state.mAttrSolid != null) { - a = t.resolveAttributes(state.mAttrSolid, R.styleable.GradientDrawableSolid, 0, 0); + a = t.resolveAttributes(state.mAttrSolid, R.styleable.GradientDrawableSolid); // TODO: updateGradientDrawableSolid(a); a.recycle(); } if (state.mAttrStroke != null) { - a = t.resolveAttributes(state.mAttrStroke, R.styleable.GradientDrawableStroke, 0, 0); + a = t.resolveAttributes(state.mAttrStroke, R.styleable.GradientDrawableStroke); // TODO: updateGradientDrawableStroke(a); a.recycle(); } if (state.mAttrCorners != null) { - a = t.resolveAttributes(state.mAttrCorners, R.styleable.DrawableCorners, 0, 0); + a = t.resolveAttributes(state.mAttrCorners, R.styleable.DrawableCorners); // TODO: updateDrawableCorners(a); a.recycle(); } if (state.mAttrPadding != null) { - a = t.resolveAttributes(state.mAttrPadding, R.styleable.GradientDrawablePadding, 0, 0); + a = t.resolveAttributes(state.mAttrPadding, R.styleable.GradientDrawablePadding); // TODO: updateGradientDrawablePadding(a); a.recycle(); } diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java index 639d719..7847aad 100644 --- a/graphics/java/android/graphics/drawable/LayerDrawable.java +++ b/graphics/java/android/graphics/drawable/LayerDrawable.java @@ -229,7 +229,7 @@ public class LayerDrawable extends Drawable implements Drawable.Callback { final int[] themeAttrs = state.mThemeAttrs; if (themeAttrs != null) { - final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.LayerDrawable, 0, 0); + final TypedArray a = t.resolveAttributes(themeAttrs, R.styleable.LayerDrawable); updateStateFromTypedArray(a); a.recycle(); } diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 21992ce..3e09707 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -72,8 +72,8 @@ public class NinePatchDrawable extends Drawable { private int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; // These are scaled to match the target density. - private int mBitmapWidth; - private int mBitmapHeight; + private int mBitmapWidth = -1; + private int mBitmapHeight = -1; NinePatchDrawable() { mNinePatchState = new NinePatchState(); @@ -209,7 +209,7 @@ public class NinePatchDrawable extends Drawable { } private void setNinePatch(NinePatch ninePatch) { - if (ninePatch != mNinePatch) { + if (mNinePatch != ninePatch) { mNinePatch = ninePatch; if (ninePatch != null) { computeBitmapSize(); @@ -339,7 +339,7 @@ public class NinePatchDrawable extends Drawable { public void setTint(ColorStateList tint) { if (mNinePatchState.mTint != tint) { mNinePatchState.mTint = tint; - updateTintFilter(); + computeTintFilter(); invalidateSelf(); } } @@ -362,23 +362,20 @@ public class NinePatchDrawable extends Drawable { public void setTintMode(Mode tintMode) { if (mNinePatchState.mTintMode != tintMode) { mNinePatchState.mTintMode = tintMode; - updateTintFilter(); + computeTintFilter(); invalidateSelf(); } } - /** - * Ensures the tint filter is consistent with the current tint color and - * mode. - */ - private void updateTintFilter() { - final ColorStateList tint = mNinePatchState.mTint; - final Mode tintMode = mNinePatchState.mTintMode; - if (tint != null && tintMode != null) { - if (mTintFilter == null) { - mTintFilter = new PorterDuffColorFilter(0, tintMode); + private void computeTintFilter() { + final NinePatchState state = mNinePatchState; + if (state.mTint != null && state.mTintMode != null) { + final int color = state.mTint.getColorForState(getState(), 0); + if (mTintFilter != null) { + mTintFilter.setColor(color); + mTintFilter.setMode(state.mTintMode); } else { - mTintFilter.setMode(tintMode); + mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); } } else { mTintFilter = null; @@ -422,38 +419,28 @@ public class NinePatchDrawable extends Drawable { throws XmlPullParserException, IOException { super.inflate(r, parser, attrs, theme); - final TypedArray a = obtainAttributes( - r, theme, attrs, R.styleable.NinePatchDrawable); - inflateStateFromTypedArray(a); + final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.NinePatchDrawable); + updateStateFromTypedArray(a); a.recycle(); } /** - * Initializes the constant state from the values in the typed array. + * Updates the constant state from the values in the typed array. */ - private void inflateStateFromTypedArray(TypedArray a) throws XmlPullParserException { + private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException { final Resources r = a.getResources(); - final NinePatchState ninePatchState = mNinePatchState; + final NinePatchState state = mNinePatchState; // Extract the theme attributes, if any. final int[] themeAttrs = a.extractThemeAttrs(); - ninePatchState.mThemeAttrs = themeAttrs; + state.mThemeAttrs = themeAttrs; - if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_dither] == 0) { - final boolean dither = a.getBoolean( - R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER); - ninePatchState.mDither = dither; - } - - if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_src] == 0) { - final int id = a.getResourceId(R.styleable.NinePatchDrawable_src, 0); - if (id == 0) { - throw new XmlPullParserException(a.getPositionDescription() + - ": <nine-patch> requires a valid src attribute"); - } + state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, state.mDither); + final int srcResId = a.getResourceId(R.styleable.NinePatchDrawable_src, 0); + if (srcResId != 0) { final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inDither = !ninePatchState.mDither; + options.inDither = !state.mDither; options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi; final Rect padding = new Rect(); @@ -462,7 +449,7 @@ public class NinePatchDrawable extends Drawable { try { final TypedValue value = new TypedValue(); - final InputStream is = r.openRawResource(id, value); + final InputStream is = r.openRawResource(srcResId, value); bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options); @@ -479,40 +466,30 @@ public class NinePatchDrawable extends Drawable { ": <nine-patch> requires a valid 9-patch source image"); } - final NinePatch ninePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk()); - ninePatchState.mNinePatch = ninePatch; - ninePatchState.mPadding = padding; - ninePatchState.mOpticalInsets = Insets.of(opticalInsets); - } - - if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_autoMirrored] == 0) { - final boolean autoMirrored = a.getBoolean( - R.styleable.NinePatchDrawable_autoMirrored, false); - ninePatchState.mAutoMirrored = autoMirrored; + state.mNinePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk()); + state.mPadding = padding; + state.mOpticalInsets = Insets.of(opticalInsets); } - if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_tintMode] == 0) { - final int tintModeValue = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1); - ninePatchState.mTintMode = Drawable.parseTintMode(tintModeValue, Mode.SRC_IN); - } + state.mAutoMirrored = a.getBoolean( + R.styleable.NinePatchDrawable_autoMirrored, state.mAutoMirrored); + state.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, state.mBaseAlpha); - if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_tint] == 0) { - ninePatchState.mTint = a.getColorStateList(R.styleable.NinePatchDrawable_tint); - if (ninePatchState.mTint != null) { - final int color = ninePatchState.mTint.getColorForState(getState(), 0); - mTintFilter = new PorterDuffColorFilter(color, ninePatchState.mTintMode); - } + final int tintMode = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1); + if (tintMode != -1) { + state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN); } - if (themeAttrs == null || themeAttrs[R.styleable.NinePatchDrawable_alpha] == 0) { - ninePatchState.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, 1.0f); + final ColorStateList tint = a.getColorStateList(R.styleable.NinePatchDrawable_tint); + if (tint != null) { + state.mTint = tint; } - // Apply the constant state to the paint. - initializeWithState(ninePatchState, r); + // Update local properties. + initializeWithState(state, r); // Push density applied by setNinePatchState into state. - ninePatchState.mTargetDensity = mTargetDensity; + state.mTargetDensity = mTargetDensity; } @Override @@ -520,98 +497,20 @@ public class NinePatchDrawable extends Drawable { super.applyTheme(t); final NinePatchState state = mNinePatchState; - if (state == null) { - throw new RuntimeException("Can't apply theme to <nine-patch> with no constant state"); + if (state == null || state.mThemeAttrs == null) { + return; } - final int[] themeAttrs = state.mThemeAttrs; - if (themeAttrs != null) { - final TypedArray a = t.resolveAttributes( - themeAttrs, R.styleable.NinePatchDrawable, 0, 0); + final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.NinePatchDrawable); + try { updateStateFromTypedArray(a); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } finally { a.recycle(); } } - /** - * Updates the constant state from the values in the typed array. - */ - private void updateStateFromTypedArray(TypedArray a) { - final Resources r = a.getResources(); - final NinePatchState state = mNinePatchState; - - if (a.hasValue(R.styleable.NinePatchDrawable_dither)) { - state.mDither = a.getBoolean(R.styleable.NinePatchDrawable_dither, DEFAULT_DITHER); - } - - if (a.hasValue(R.styleable.NinePatchDrawable_autoMirrored)) { - state.mAutoMirrored = a.getBoolean(R.styleable.NinePatchDrawable_autoMirrored, false); - } - - if (a.hasValue(R.styleable.NinePatchDrawable_src)) { - final int id = a.getResourceId(R.styleable.NinePatchDrawable_src, 0); - if (id == 0) { - throw new RuntimeException(a.getPositionDescription() + - ": <nine-patch> requires a valid src attribute"); - } - - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inDither = !state.mDither; - options.inScreenDensity = r.getDisplayMetrics().noncompatDensityDpi; - - final Rect padding = new Rect(); - final Rect opticalInsets = new Rect(); - Bitmap bitmap = null; - - try { - final TypedValue value = new TypedValue(); - final InputStream is = r.openRawResource(id, value); - - bitmap = BitmapFactory.decodeResourceStream(r, value, is, padding, options); - - is.close(); - } catch (IOException e) { - // Ignore - } - - if (bitmap == null) { - throw new RuntimeException(a.getPositionDescription() + - ": <nine-patch> requires a valid src attribute"); - } else if (bitmap.getNinePatchChunk() == null) { - throw new RuntimeException(a.getPositionDescription() + - ": <nine-patch> requires a valid 9-patch source image"); - } - - state.mNinePatch = new NinePatch(bitmap, bitmap.getNinePatchChunk()); - state.mPadding = padding; - state.mOpticalInsets = Insets.of(opticalInsets); - } - - if (a.hasValue(R.styleable.NinePatchDrawable_tintMode)) { - final int modeValue = a.getInt(R.styleable.NinePatchDrawable_tintMode, -1); - state.mTintMode = Drawable.parseTintMode(modeValue, Mode.SRC_IN); - } - - if (a.hasValue(R.styleable.NinePatchDrawable_tint)) { - final ColorStateList tint = a.getColorStateList(R.styleable.NinePatchDrawable_tint); - if (tint != null) { - state.mTint = tint; - final int color = tint.getColorForState(getState(), 0); - mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); - } - } - - if (a.hasValue(R.styleable.NinePatchDrawable_alpha)) { - state.mBaseAlpha = a.getFloat(R.styleable.NinePatchDrawable_alpha, 1.0f); - } - - // Apply the constant state to the paint. - initializeWithState(state, r); - - // Push density applied by setNinePatchState into state. - state.mTargetDensity = mTargetDensity; - } - @Override public boolean canApplyTheme() { return mNinePatchState != null && mNinePatchState.mThemeAttrs != null; @@ -705,17 +604,19 @@ public class NinePatchDrawable extends Drawable { } final static class NinePatchState extends ConstantState { - NinePatch mNinePatch; - ColorStateList mTint; + // Values loaded during inflation. + int[] mThemeAttrs = null; + NinePatch mNinePatch = null; + ColorStateList mTint = null; Mode mTintMode = Mode.SRC_IN; - Rect mPadding; - Insets mOpticalInsets; + Rect mPadding = null; + Insets mOpticalInsets = Insets.NONE; float mBaseAlpha = 1.0f; - boolean mDither; - int[] mThemeAttrs; - int mChangingConfigurations; + boolean mDither = DEFAULT_DITHER; int mTargetDensity = DisplayMetrics.DENSITY_DEFAULT; - boolean mAutoMirrored; + boolean mAutoMirrored = false; + + int mChangingConfigurations; NinePatchState() { // Empty constructor. @@ -786,6 +687,10 @@ public class NinePatchDrawable extends Drawable { } } + /** + * The one constructor to rule them all. This is called by all public + * constructors to set the state and initialize local properties. + */ private NinePatchDrawable(NinePatchState state, Resources res, Theme theme) { if (theme != null && state.canApplyTheme()) { mNinePatchState = new NinePatchState(state); @@ -812,14 +717,12 @@ public class NinePatchDrawable extends Drawable { setDither(state.mDither); } - if (state.mTint != null) { - final int color = state.mTint.getColorForState(getState(), 0); - mTintFilter = new PorterDuffColorFilter(color, state.mTintMode); + // Make a local copy of the padding. + if (state.mPadding != null) { + mPadding = new Rect(state.mPadding); } - final Rect statePadding = state.mPadding; - mPadding = statePadding != null ? new Rect(statePadding) : null; - + computeTintFilter(); setNinePatch(state.mNinePatch); } } diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java index 0097183..8128b5f 100644 --- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java +++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java @@ -291,7 +291,7 @@ public class TouchFeedbackDrawable extends LayerDrawable { final int[] themeAttrs = state.mTouchThemeAttrs; if (themeAttrs != null) { final TypedArray a = t.resolveAttributes( - themeAttrs, R.styleable.TouchFeedbackDrawable, 0, 0); + themeAttrs, R.styleable.TouchFeedbackDrawable); updateStateFromTypedArray(a); a.recycle(); } diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 2da8615..05658f5 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -753,7 +753,7 @@ public class VectorDrawable extends Drawable { } final TypedArray a = t.resolveAttributes( - mThemeAttrs, R.styleable.VectorDrawablePath, 0, 0); + mThemeAttrs, R.styleable.VectorDrawablePath); mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip); diff --git a/graphics/java/android/graphics/pdf/PdfDocument.java b/graphics/java/android/graphics/pdf/PdfDocument.java index f5b07c1..d603436 100644 --- a/graphics/java/android/graphics/pdf/PdfDocument.java +++ b/graphics/java/android/graphics/pdf/PdfDocument.java @@ -32,7 +32,7 @@ import java.util.List; /** * <p> * This class enables generating a PDF document from native Android content. You - * open a new document and then for every page you want to add you start a page, + * create a new document and then for every page you want to add you start a page, * write content to the page, and finish the page. After you are done with all * pages, you write the document to an output stream and close the document. * After a document is closed you should not use it anymore. Note that pages are @@ -64,7 +64,7 @@ import java.util.List; * // write the document content * document.writeTo(getOutputStream()); * - * //close the document + * // close the document * document.close(); * </pre> */ diff --git a/graphics/java/android/graphics/pdf/PdfRenderer.java b/graphics/java/android/graphics/pdf/PdfRenderer.java new file mode 100644 index 0000000..3fa3b9f --- /dev/null +++ b/graphics/java/android/graphics/pdf/PdfRenderer.java @@ -0,0 +1,391 @@ +/* + * 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.pdf; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.ParcelFileDescriptor; +import android.system.ErrnoException; +import android.system.OsConstants; +import dalvik.system.CloseGuard; +import libcore.io.Libcore; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * <p> + * This class enables rendering a PDF document. This class is not thread safe. + * </p> + * <p> + * If you want to render a PDF, you create a renderer and for every page you want + * to render, you open the page, render it, and close the page. After you are done + * with rendering, you close the renderer. After the renderer is closed it should not + * be used anymore. Note that the pages are rendered one by one, i.e. you can have + * only a single page opened at any given time. + * </p> + * <p> + * A typical use of the APIs to render a PDF looks like this: + * </p> + * <pre> + * // create a new renderer + * PdfRenderer renderer = new PdfRenderer(getSeekableFileDescriptor()); + * + * // let us just render all pages + * final int pageCount = renderer.getPageCount(); + * for (int i = 0; i < pageCount; i++) { + * Page page = renderer.openPage(i); + * Bitmap bitmap = getBitmapReuseIfPossible(page); + * + * // say we render for showing on the screen + * page.render(bitmap, getContentBoundsInBitmap(), + * getDesiredTransformation(), Page.RENDER_MODE_FOR_DISPLAY); + * + * // do stuff with the bitmap + * + * renderer.closePage(page); + * } + * + * // close the renderer + * renderer.close(); + * </pre> + * + * @see #close() + */ +public final class PdfRenderer implements AutoCloseable { + private final CloseGuard mCloseGuard = CloseGuard.get(); + + private final Point mTempPoint = new Point(); + + private final long mNativeDocument; + + private final int mPageCount; + + private ParcelFileDescriptor mInput; + + private Page mCurrentPage; + + /** @hide */ + @IntDef({ + Page.RENDER_MODE_FOR_DISPLAY, + Page.RENDER_MODE_FOR_PRINT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RenderMode {} + + /** + * Creates a new instance. + * <p> + * <strong>Note:</strong> The provided file descriptor must be <strong>seekable</strong>, + * i.e. its data being randomly accessed, e.g. pointing to a file. + * </p> + * <p> + * <strong>Note:</strong> This class takes ownership of the passed in file descriptor + * and is responsible for closing it when the renderer is closed. + * </p> + * + * @param input Seekable file descriptor to read from. + */ + public PdfRenderer(@NonNull ParcelFileDescriptor input) throws IOException { + if (input == null) { + throw new NullPointerException("input cannot be null"); + } + + final long size; + try { + Libcore.os.lseek(input.getFileDescriptor(), 0, OsConstants.SEEK_SET); + size = Libcore.os.fstat(input.getFileDescriptor()).st_size; + } catch (ErrnoException ee) { + throw new IllegalArgumentException("file descriptor not seekable"); + } + + mInput = input; + mNativeDocument = nativeCreate(mInput.getFd(), size); + mPageCount = nativeGetPageCount(mNativeDocument); + mCloseGuard.open("close"); + } + + /** + * Closes this renderer. You should not use this instance + * after this method is called. + */ + public void close() { + throwIfClosed(); + throwIfPageOpened(); + doClose(); + } + + /** + * Gets the number of pages in the document. + * + * @return The page count. + */ + public int getPageCount() { + throwIfClosed(); + return mPageCount; + } + + /** + * Gets whether the document prefers to be scaled for printing. + * You should take this info account if the document is rendered + * for printing and the target media size differs from the page + * size. + * + * @return If to scale the document. + */ + public boolean shouldScaleForPrinting() { + throwIfClosed(); + return nativeScaleForPrinting(mNativeDocument); + } + + /** + * Opens a page for rendering. + * + * @param index The page index. + * @return A page that can be rendered. + * + * @see #closePage(PdfRenderer.Page) + */ + public Page openPage(int index) { + throwIfClosed(); + throwIfPageOpened(); + mCurrentPage = new Page(index); + return mCurrentPage; + } + + /** + * Closes a page opened for rendering. + * + * @param page The page to close. + * + * @see #openPage(int) + */ + public void closePage(@NonNull Page page) { + throwIfClosed(); + throwIfNotCurrentPage(page); + throwIfCurrentPageClosed(); + mCurrentPage.close(); + mCurrentPage = null; + } + + @Override + protected void finalize() throws Throwable { + try { + mCloseGuard.warnIfOpen(); + if (mInput != null) { + doClose(); + } + } finally { + super.finalize(); + } + } + + private void doClose() { + if (mCurrentPage != null) { + mCurrentPage.close(); + mCurrentPage = null; + } + nativeClose(mNativeDocument); + try { + mInput.close(); + } catch (IOException ioe) { + /* ignore - best effort */ + } + mInput = null; + mCloseGuard.close(); + } + + private void throwIfClosed() { + if (mInput == null) { + throw new IllegalStateException("Already closed"); + } + } + + private void throwIfPageOpened() { + if (mCurrentPage != null) { + throw new IllegalStateException("Current page not closed"); + } + } + + private void throwIfCurrentPageClosed() { + if (mCurrentPage == null) { + throw new IllegalStateException("Already closed"); + } + } + + private void throwIfNotCurrentPage(Page page) { + if (page != mCurrentPage) { + throw new IllegalArgumentException("Page not from document"); + } + } + + /** + * This class represents a PDF document page for rendering. + */ + public final class Page { + + /** + * Mode to render the content for display on a screen. + */ + public static final int RENDER_MODE_FOR_DISPLAY = 1; + + /** + * Mode to render the content for printing. + */ + public static final int RENDER_MODE_FOR_PRINT = 2; + + private final int mIndex; + private final int mWidth; + private final int mHeight; + + private long mNativePage; + + private Page(int index) { + Point size = mTempPoint; + mNativePage = nativeOpenPageAndGetSize(mNativeDocument, index, size); + mIndex = index; + mWidth = size.x; + mHeight = size.y; + } + + /** + * Gets the page index. + * + * @return The index. + */ + public int getIndex() { + return mIndex; + } + + /** + * Gets the page width in points (1/72"). + * + * @return The width in points. + */ + public int getWidth() { + return mWidth; + } + + /** + * Gets the page height in points (1/72"). + * + * @return The height in points. + */ + public int getHeight() { + return mHeight; + } + + /** + * Renders a page to a bitmap. + * <p> + * You may optionally specify a rectangular clip in the bitmap bounds. No rendering + * outside the clip will be performed, hence it is your responsibility to initialize + * the bitmap outside the clip. + * </p> + * <p> + * You may optionally specify a matrix to transform the content from page coordinates + * which are in points (1/72") to bitmap coordintates which are in pixels. If this + * matrix is not provided this method will apply a transformation that will fit the + * whole page to the destination clip if profided or the destination bitmap if no + * clip is provided. + * </p> + * <p> + * The clip and transformation are useful for implementing tile rendering where the + * destination bitmap contains a portion of the image, for example when zooming. + * Another useful application is for printing where the size of the bitmap holding + * the page is too large and a client can render the page in stripes. + * </p> + * <p> + * <strong>Note: </strong> The destination bitmap format must be + * {@link Config#ARGB_8888 ARGB}. + * </p> + * <p> + * <strong>Note: </strong> The optional transformation matrix must be affine as per + * {@link android.graphics.Matrix#isAffine()}. Hence, you can specify rotation, scaling, + * translation but not a perspective transformation. + * </p> + * + * @param destination Destination bitmap to which to render. + * @param destClip Optional clip in the bitmap bounds. + * @param transform Optional transformation to apply when rendering. + * @param renderMode The render mode. + * + * @see #RENDER_MODE_FOR_DISPLAY + * @see #RENDER_MODE_FOR_PRINT + */ + public void render(@NonNull Bitmap destination, @Nullable Rect destClip, + @Nullable Matrix transform, @RenderMode int renderMode) { + if (destination.getConfig() != Config.ARGB_8888) { + throw new IllegalArgumentException("Unsupported pixel format"); + } + + if (destClip != null) { + if (destClip.left < 0 || destClip.top < 0 + || destClip.right > destination.getWidth() + || destClip.bottom > destination.getHeight()) { + throw new IllegalArgumentException("destBounds not in destination"); + } + } + + if (transform != null && !transform.isAffine()) { + throw new IllegalArgumentException("transform not affine"); + } + + if (renderMode != RENDER_MODE_FOR_PRINT && renderMode != RENDER_MODE_FOR_DISPLAY) { + throw new IllegalArgumentException("Unsupported render mode"); + } + + if (renderMode == RENDER_MODE_FOR_PRINT && renderMode == RENDER_MODE_FOR_DISPLAY) { + throw new IllegalArgumentException("Only single render mode supported"); + } + + final int contentLeft = (destClip != null) ? destClip.left : 0; + final int contentTop = (destClip != null) ? destClip.top : 0; + final int contentRight = (destClip != null) ? destClip.right + : destination.getWidth(); + final int contentBottom = (destClip != null) ? destClip.bottom + : destination.getHeight(); + + final long transformPtr = (transform != null) ? transform.native_instance : 0; + + nativeRenderPage(mNativeDocument, mNativePage, destination.mNativeBitmap, contentLeft, + contentTop, contentRight, contentBottom, transformPtr, renderMode); + } + + void close() { + nativeClosePage(mNativePage); + mNativePage = 0; + } + } + + private static native long nativeCreate(int fd, long size); + private static native void nativeClose(long documentPtr); + private static native int nativeGetPageCount(long documentPtr); + private static native boolean nativeScaleForPrinting(long documentPtr); + private static native void nativeRenderPage(long documentPtr, long pagePtr, long destPtr, + int destLeft, int destTop, int destRight, int destBottom, long matrixPtr, int renderMode); + private static native long nativeOpenPageAndGetSize(long documentPtr, int pageIndex, + Point outSize); + private static native void nativeClosePage(long pagePtr); +} diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index a033f86..83eedfb 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -27,31 +27,48 @@ namespace android { namespace uirenderer { /************************************************************ - * Base animator + * BaseRenderNodeAnimator ************************************************************/ -BaseAnimator::BaseAnimator() - : mInterpolator(0) - , mPlayState(PENDING) +BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) + : mFinalValue(finalValue) + , mDeltaValue(0) + , mFromValue(0) + , mInterpolator(0) + , mPlayState(NEEDS_START) , mStartTime(0) - , mDuration(300) { - + , mDuration(300){ } -BaseAnimator::~BaseAnimator() { +BaseRenderNodeAnimator::~BaseRenderNodeAnimator() { setInterpolator(NULL); } -void BaseAnimator::setInterpolator(Interpolator* interpolator) { +void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) { delete mInterpolator; mInterpolator = interpolator; } -void BaseAnimator::setDuration(nsecs_t duration) { +void BaseRenderNodeAnimator::setDuration(nsecs_t duration) { mDuration = duration; } -bool BaseAnimator::animateFrame(TreeInfo& info) { +void BaseRenderNodeAnimator::setStartValue(float value) { + LOG_ALWAYS_FATAL_IF(mPlayState != NEEDS_START, + "Cannot set the start value after the animator has started!"); + mFromValue = value; + mDeltaValue = (mFinalValue - mFromValue); + mPlayState = PENDING; +} + +void BaseRenderNodeAnimator::setupStartValueIfNecessary(RenderNode* target, TreeInfo& info) { + if (mPlayState == NEEDS_START) { + setStartValue(getValue(target)); + mPlayState = PENDING; + } +} + +bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) { if (mPlayState == PENDING) { mPlayState = RUNNING; mStartTime = info.frameTimeMs; @@ -59,7 +76,6 @@ bool BaseAnimator::animateFrame(TreeInfo& info) { if (!mInterpolator) { setInterpolator(Interpolator::createDefaultInterpolator()); } - onAnimationStarted(); } float fraction = 1.0f; @@ -71,17 +87,16 @@ bool BaseAnimator::animateFrame(TreeInfo& info) { } } fraction = mInterpolator->interpolate(fraction); - onAnimationUpdated(fraction); + setValue(target, mFromValue + (mDeltaValue * fraction)); if (mPlayState == FINISHED) { - onAnimationFinished(); callOnFinishedListener(info); return true; } return false; } -void BaseAnimator::callOnFinishedListener(TreeInfo& info) { +void BaseRenderNodeAnimator::callOnFinishedListener(TreeInfo& info) { if (mListener.get()) { if (!info.animationHook) { mListener->onAnimationFinished(this); @@ -92,70 +107,49 @@ void BaseAnimator::callOnFinishedListener(TreeInfo& info) { } /************************************************************ - * BaseRenderNodeAnimator - ************************************************************/ - -BaseRenderNodeAnimator::BaseRenderNodeAnimator( - BaseRenderNodeAnimator::DeltaValueType deltaType, float delta) - : mTarget(0) - , mDeltaValueType(deltaType) - , mDeltaValue(delta) - , mFromValue(-1) { -} - -bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) { - mTarget = target; - bool finished = animateFrame(info); - mTarget = NULL; - return finished; -} - -void BaseRenderNodeAnimator::onAnimationStarted() { - mFromValue = getValue(); - - if (mDeltaValueType == BaseRenderNodeAnimator::ABSOLUTE) { - mDeltaValue = (mDeltaValue - mFromValue); - mDeltaValueType = BaseRenderNodeAnimator::DELTA; - } -} - -void BaseRenderNodeAnimator::onAnimationUpdated(float fraction) { - float value = mFromValue + (mDeltaValue * fraction); - setValue(value); -} - -/************************************************************ * RenderPropertyAnimator ************************************************************/ +struct RenderPropertyAnimator::PropertyAccessors { + RenderNode::DirtyPropertyMask dirtyMask; + GetFloatProperty getter; + SetFloatProperty setter; +}; + // Maps RenderProperty enum to accessors const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = { - {&RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, - {&RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, - {&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, - {&RenderProperties::getScaleX, &RenderProperties::setScaleX }, - {&RenderProperties::getScaleY, &RenderProperties::setScaleY }, - {&RenderProperties::getRotation, &RenderProperties::setRotation }, - {&RenderProperties::getRotationX, &RenderProperties::setRotationX }, - {&RenderProperties::getRotationY, &RenderProperties::setRotationY }, - {&RenderProperties::getX, &RenderProperties::setX }, - {&RenderProperties::getY, &RenderProperties::setY }, - {&RenderProperties::getZ, &RenderProperties::setZ }, - {&RenderProperties::getAlpha, &RenderProperties::setAlpha }, + {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, + {RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, + {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, + {RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX }, + {RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY }, + {RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation }, + {RenderNode::ROTATION_X, &RenderProperties::getRotationX, &RenderProperties::setRotationX }, + {RenderNode::ROTATION_Y, &RenderProperties::getRotationY, &RenderProperties::setRotationY }, + {RenderNode::X, &RenderProperties::getX, &RenderProperties::setX }, + {RenderNode::Y, &RenderProperties::getY, &RenderProperties::setY }, + {RenderNode::Z, &RenderProperties::getZ, &RenderProperties::setZ }, + {RenderNode::ALPHA, &RenderProperties::getAlpha, &RenderProperties::setAlpha }, }; -RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, - DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) - , mPropertyAccess(PROPERTY_ACCESSOR_LUT[property]) { +RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float finalValue) + : BaseRenderNodeAnimator(finalValue) + , mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) { +} + +void RenderPropertyAnimator::onAttached(RenderNode* target) { + if (target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { + setStartValue((target->stagingProperties().*mPropertyAccess->getter)()); + } + (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); } -float RenderPropertyAnimator::getValue() const { - return (target()->animatorProperties().*mPropertyAccess.getter)(); +float RenderPropertyAnimator::getValue(RenderNode* target) const { + return (target->properties().*mPropertyAccess->getter)(); } -void RenderPropertyAnimator::setValue(float value) { - (target()->animatorProperties().*mPropertyAccess.setter)(value); +void RenderPropertyAnimator::setValue(RenderNode* target, float value) { + (target->animatorProperties().*mPropertyAccess->setter)(value); } /************************************************************ @@ -163,16 +157,16 @@ void RenderPropertyAnimator::setValue(float value) { ************************************************************/ CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator( - CanvasPropertyPrimitive* property, DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) + CanvasPropertyPrimitive* property, float finalValue) + : BaseRenderNodeAnimator(finalValue) , mProperty(property) { } -float CanvasPropertyPrimitiveAnimator::getValue() const { +float CanvasPropertyPrimitiveAnimator::getValue(RenderNode* target) const { return mProperty->value; } -void CanvasPropertyPrimitiveAnimator::setValue(float value) { +void CanvasPropertyPrimitiveAnimator::setValue(RenderNode* target, float value) { mProperty->value = value; } @@ -181,14 +175,13 @@ void CanvasPropertyPrimitiveAnimator::setValue(float value) { ************************************************************/ CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator( - CanvasPropertyPaint* property, PaintField field, - DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) + CanvasPropertyPaint* property, PaintField field, float finalValue) + : BaseRenderNodeAnimator(finalValue) , mProperty(property) , mField(field) { } -float CanvasPropertyPaintAnimator::getValue() const { +float CanvasPropertyPaintAnimator::getValue(RenderNode* target) const { switch (mField) { case STROKE_WIDTH: return mProperty->value.getStrokeWidth(); @@ -204,7 +197,7 @@ static uint8_t to_uint8(float value) { return static_cast<uint8_t>( c < 0 ? 0 : c > 255 ? 255 : c ); } -void CanvasPropertyPaintAnimator::setValue(float value) { +void CanvasPropertyPaintAnimator::setValue(RenderNode* target, float value) { switch (mField) { case STROKE_WIDTH: mProperty->value.setStrokeWidth(value); diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 52a1807..fe88cbf 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -33,16 +33,14 @@ class RenderProperties; class AnimationListener : public VirtualLightRefBase { public: - ANDROID_API virtual void onAnimationFinished(BaseAnimator*) = 0; + ANDROID_API virtual void onAnimationFinished(BaseRenderNodeAnimator*) = 0; protected: ANDROID_API virtual ~AnimationListener() {} }; -// Helper class to contain generic animator helpers -class BaseAnimator : public VirtualLightRefBase { - PREVENT_COPY_AND_ASSIGN(BaseAnimator); +class BaseRenderNodeAnimator : public VirtualLightRefBase { + PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator); public: - ANDROID_API void setInterpolator(Interpolator* interpolator); ANDROID_API void setDuration(nsecs_t durationInMs); ANDROID_API nsecs_t duration() { return mDuration; } @@ -50,31 +48,38 @@ public: mListener = listener; } + ANDROID_API virtual void onAttached(RenderNode* target) {} + + // Guaranteed to happen before the staging push + void setupStartValueIfNecessary(RenderNode* target, TreeInfo& info); + + bool animate(RenderNode* target, TreeInfo& info); + bool isFinished() { return mPlayState == FINISHED; } + float finalValue() { return mFinalValue; } protected: - BaseAnimator(); - virtual ~BaseAnimator(); - - // This is the main animation entrypoint that subclasses should call - // to generate the onAnimation* lifecycle events - // Returns true if the animation has finished, false otherwise - bool animateFrame(TreeInfo& info); + BaseRenderNodeAnimator(float finalValue); + virtual ~BaseRenderNodeAnimator(); - // Called when PlayState switches from PENDING to RUNNING - virtual void onAnimationStarted() {} - virtual void onAnimationUpdated(float fraction) = 0; - virtual void onAnimationFinished() {} + void setStartValue(float value); + virtual float getValue(RenderNode* target) const = 0; + virtual void setValue(RenderNode* target, float value) = 0; private: void callOnFinishedListener(TreeInfo& info); enum PlayState { + NEEDS_START, PENDING, RUNNING, FINISHED, }; + float mFinalValue; + float mDeltaValue; + float mFromValue; + Interpolator* mInterpolator; PlayState mPlayState; long mStartTime; @@ -83,42 +88,6 @@ private: sp<AnimationListener> mListener; }; -class BaseRenderNodeAnimator : public BaseAnimator { -public: - // Since the UI thread doesn't necessarily know what the current values - // actually are and thus can't do the calculations, this is used to inform - // the animator how to lazy-resolve the input value - enum DeltaValueType { - // The delta value represents an absolute value endpoint - // mDeltaValue needs to be recalculated to be mDelta = (mDelta - fromValue) - // in onAnimationStarted() - ABSOLUTE = 0, - // The final value represents an offset from the current value - // No recalculation is needed - DELTA, - }; - - bool animate(RenderNode* target, TreeInfo& info); - -protected: - BaseRenderNodeAnimator(DeltaValueType deltaType, float deltaValue); - - RenderNode* target() const { return mTarget; } - virtual float getValue() const = 0; - virtual void setValue(float value) = 0; - -private: - virtual void onAnimationStarted(); - virtual void onAnimationUpdated(float fraction); - - // mTarget is only valid inside animate() - RenderNode* mTarget; - - BaseRenderNodeAnimator::DeltaValueType mDeltaValueType; - float mDeltaValue; - float mFromValue; -}; - class RenderPropertyAnimator : public BaseRenderNodeAnimator { public: enum RenderProperty { @@ -136,23 +105,20 @@ public: ALPHA, }; - ANDROID_API RenderPropertyAnimator(RenderProperty property, - DeltaValueType deltaType, float deltaValue); + ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue); + + ANDROID_API virtual void onAttached(RenderNode* target); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: typedef void (RenderProperties::*SetFloatProperty)(float value); typedef float (RenderProperties::*GetFloatProperty)() const; - struct PropertyAccessors { - GetFloatProperty getter; - SetFloatProperty setter; - }; - - PropertyAccessors mPropertyAccess; + struct PropertyAccessors; + const PropertyAccessors* mPropertyAccess; static const PropertyAccessors PROPERTY_ACCESSOR_LUT[]; }; @@ -160,10 +126,10 @@ private: class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator { public: ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property, - DeltaValueType deltaType, float deltaValue); + float finalValue); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: sp<CanvasPropertyPrimitive> mProperty; }; @@ -176,10 +142,10 @@ public: }; ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, - PaintField field, DeltaValueType deltaType, float deltaValue); + PaintField field, float finalValue); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: sp<CanvasPropertyPaint> mProperty; PaintField mField; diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 45b6624..3016814 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -28,6 +28,7 @@ #include "DeferredDisplayList.h" #include "DisplayListOp.h" #include "OpenGLRenderer.h" +#include "utils/MathUtils.h" #if DEBUG_DEFER #define DEFER_LOGD(...) ALOGD(__VA_ARGS__) @@ -146,10 +147,6 @@ private: mergeid_t mMergeId; }; -// compare alphas approximately, with a small margin -#define NEQ_FALPHA(lhs, rhs) \ - fabs((float)lhs - (float)rhs) > 0.001f - class MergingDrawBatch : public DrawBatch { public: MergingDrawBatch(DeferInfo& deferInfo, int width, int height) : @@ -196,7 +193,11 @@ public: const DeferredDisplayState* lhs = state; const DeferredDisplayState* rhs = mOps[0].state; - if (NEQ_FALPHA(lhs->mAlpha, rhs->mAlpha)) return false; + if (!MathUtils::areEqual(lhs->mAlpha, rhs->mAlpha)) return false; + + // Identical round rect clip state means both ops will clip in the same way, or not at all. + // As the state objects are const, we can compare their pointers to determine mergeability + if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false; /* Clipping compatibility check * diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index fca3588..48489c2 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -64,6 +64,7 @@ public: mat4 mMatrix; DrawModifiers mDrawModifiers; float mAlpha; + const RoundRectClipState* mRoundRectClipState; }; class OpStatePair { diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 2391e80..a4bce3a 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -57,9 +57,6 @@ DisplayListData* DisplayListRenderer::finishRecording() { } void DisplayListRenderer::setViewport(int width, int height) { - // TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used - mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1); - initializeViewport(width, height); } diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 9606e58..de2fcf4 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -214,7 +214,7 @@ void Layer::defer() { DeferStateStruct deferredState(*deferredList, *renderer, RenderNode::kReplayFlag_ClipChildren); - renderer->initViewport(width, height); + renderer->initializeViewport(width, height); renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index e0ac2ba..c82197c 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -40,7 +40,7 @@ LayerRenderer::~LayerRenderer() { } void LayerRenderer::setViewport(int width, int height) { - initViewport(width, height); + initializeViewport(width, height); } status_t LayerRenderer::prepareDirty(float left, float top, float right, float bottom, diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 20b038d..7993c0f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -162,7 +162,7 @@ void OpenGLRenderer::initProperties() { /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::setViewport(int width, int height) { - initViewport(width, height); + initializeViewport(width, height); glDisable(GL_DITHER); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -170,12 +170,6 @@ void OpenGLRenderer::setViewport(int width, int height) { glEnableVertexAttribArray(Program::kBindingPosition); } -void OpenGLRenderer::initViewport(int width, int height) { - mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1); - - initializeViewport(width, height); -} - void OpenGLRenderer::setupFrameState(float left, float top, float right, float bottom, bool opaque) { mCaches.clearGarbage(); @@ -244,7 +238,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { if (!opaque || mCountOverdraw) { mCaches.enableScissor(); - mCaches.setScissor(left, currentSnapshot()->height - bottom, right - left, bottom - top); + mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top); glClear(GL_COLOR_BUFFER_BIT); return DrawGlInfo::kStatusDrew; } @@ -270,7 +264,7 @@ void OpenGLRenderer::startTilingCurrentClip(bool opaque) { clip = &(snapshot->layer->clipRect); } - startTiling(*clip, snapshot->height, opaque); + startTiling(*clip, getViewportHeight(), opaque); } } @@ -333,7 +327,7 @@ void OpenGLRenderer::interrupt() { void OpenGLRenderer::resume() { const Snapshot* snapshot = currentSnapshot(); - glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); + glViewport(0, 0, getViewportWidth(), getViewportHeight()); glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); debugOverdraw(true, false); @@ -354,9 +348,8 @@ void OpenGLRenderer::resume() { } void OpenGLRenderer::resumeAfterLayer() { - const Snapshot* snapshot = currentSnapshot(); - glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); - glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); + glViewport(0, 0, getViewportWidth(), getViewportHeight()); + glBindFramebuffer(GL_FRAMEBUFFER, currentSnapshot()->fbo); debugOverdraw(true, false); mCaches.resetScissor(); @@ -381,8 +374,8 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { info.clipRight = clip.right; info.clipBottom = clip.bottom; info.isLayer = hasLayer(); - info.width = currentSnapshot()->viewport.getWidth(); - info.height = currentSnapshot()->height; + info.width = getViewportWidth(); + info.height = getViewportHeight(); currentTransform()->copyTo(&info.transform[0]); bool dirtyClip = mDirtyClip; @@ -437,7 +430,7 @@ void OpenGLRenderer::renderOverdraw() { const Rect* clip = &mTilingClip; mCaches.enableScissor(); - mCaches.setScissor(clip->left, firstSnapshot()->height - clip->bottom, + mCaches.setScissor(clip->left, firstSnapshot()->getViewportHeight() - clip->bottom, clip->right - clip->left, clip->bottom - clip->top); // 1x overdraw @@ -621,14 +614,12 @@ void OpenGLRenderer::flushLayerUpdates() { /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { - bool restoreOrtho = removed.flags & Snapshot::kFlagDirtyOrtho; + bool restoreViewport = removed.flags & Snapshot::kFlagIsFboLayer; bool restoreClip = removed.flags & Snapshot::kFlagClipSet; bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer; - if (restoreOrtho) { - const Rect& r = restored.viewport; - glViewport(r.left, r.top, r.right, r.bottom); - mProjectionMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'? + if (restoreViewport) { + glViewport(0, 0, getViewportWidth(), getViewportHeight()); } if (restoreClip) { @@ -671,7 +662,7 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool // When the layer is not an FBO, we may use glCopyTexImage so we // need to make sure the layer does not extend outside the bounds // of the framebuffer - if (!bounds.intersect(currentSnapshot()->previous->viewport)) { + if (!bounds.intersect(Rect(0, 0, getViewportWidth(), getViewportHeight()))) { bounds.setEmpty(); } else if (fboLayer) { clip.set(bounds); @@ -719,7 +710,8 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float if (!currentSnapshot()->isIgnored()) { mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); - mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight()); + mSnapshot->roundRectClipState = NULL; } } @@ -831,8 +823,9 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto layer->setEmpty(false); } - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, - mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight()); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + bounds.left, getViewportHeight() - bounds.bottom, + bounds.getWidth(), bounds.getHeight()); // Enqueue the buffer coordinates to clear the corresponding region later mLayers.push(new Rect(bounds)); @@ -847,14 +840,12 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { layer->setFbo(mCaches.fboCache.get()); mSnapshot->region = &mSnapshot->layer->region; - mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer | - Snapshot::kFlagDirtyOrtho; + mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer; mSnapshot->fbo = layer->getFbo(); mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); - mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - mSnapshot->height = bounds.getHeight(); - mSnapshot->orthoMatrix.load(mProjectionMatrix); + mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight()); + mSnapshot->roundRectClipState = NULL; endTiling(); debugOverdraw(false, false); @@ -883,9 +874,6 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { // Change the ortho projection glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - - mProjectionMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); - return true; } @@ -904,7 +892,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto bool clipRequired = false; calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom, - &clipRequired, false); // safely ignore return, should never be rejected + &clipRequired, NULL, false); // safely ignore return, should never be rejected mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); if (fboLayer) { @@ -1379,6 +1367,9 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef state.mMatrix.load(*currentMatrix); state.mDrawModifiers = mDrawModifiers; state.mAlpha = currentSnapshot()->alpha; + + // always store/restore, since it's just a pointer + state.mRoundRectClipState = currentSnapshot()->roundRectClipState; return false; } @@ -1386,6 +1377,7 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool setMatrix(state.mMatrix); mSnapshot->alpha = state.mAlpha; mDrawModifiers = state.mDrawModifiers; + mSnapshot->roundRectClipState = state.mRoundRectClipState; if (state.mClipValid && !skipClipRestore) { mSnapshot->setClip(state.mClip.left, state.mClip.top, @@ -1419,7 +1411,7 @@ void OpenGLRenderer::setScissorFromClip() { Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); - if (mCaches.setScissor(clip.left, currentSnapshot()->height - clip.bottom, + if (mCaches.setScissor(clip.left, getViewportHeight() - clip.bottom, clip.getWidth(), clip.getHeight())) { mDirtyClip = false; } @@ -1461,7 +1453,7 @@ void OpenGLRenderer::setStencilFromClip() { mCaches.stencil.enableWrite(); - // Clear the stencil but first make sure we restrict drawing + // Clear and update the stencil, but first make sure we restrict drawing // to the region's bounds bool resetScissor = mCaches.enableScissor(); if (resetScissor) { @@ -1469,7 +1461,10 @@ void OpenGLRenderer::setStencilFromClip() { setScissorFromClip(); } mCaches.stencil.clear(); - if (resetScissor) mCaches.disableScissor(); + + // stash and disable the outline clip state, since stencil doesn't account for outline + bool storedSkipOutlineClip = mSkipOutlineClip; + mSkipOutlineClip = true; SkPaint paint; paint.setColor(0xff000000); @@ -1482,6 +1477,8 @@ void OpenGLRenderer::setStencilFromClip() { // The last parameter is important: we are not drawing in the color buffer // so we don't want to dirty the current layer, if any drawRegionRects(*(currentSnapshot()->clipRegion), paint, false); + if (resetScissor) mCaches.disableScissor(); + mSkipOutlineClip = storedSkipOutlineClip; mCaches.stencil.enableTest(); @@ -1506,7 +1503,6 @@ void OpenGLRenderer::setStencilFromClip() { */ bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom, const SkPaint* paint) { - bool clipRequired = false; bool snapOut = paint && paint->isAntiAlias(); if (paint && paint->getStyle() != SkPaint::kFill_Style) { @@ -1517,13 +1513,17 @@ bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, bottom += outset; } - if (calculateQuickRejectForScissor(left, top, right, bottom, &clipRequired, snapOut)) { + bool clipRequired = false; + bool roundRectClipRequired = false; + if (calculateQuickRejectForScissor(left, top, right, bottom, + &clipRequired, &roundRectClipRequired, snapOut)) { return true; } if (!isRecording()) { // not quick rejected, so enable the scissor if clipRequired mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); + mSkipOutlineClip = !roundRectClipRequired; } return false; } @@ -1680,6 +1680,18 @@ void OpenGLRenderer::setupDrawBlending(const SkPaint* paint, bool blend, bool sw void OpenGLRenderer::setupDrawProgram() { useProgram(mCaches.programCache.get(mDescription)); + if (mDescription.hasRoundRectClip) { + // TODO: avoid doing this repeatedly, stashing state pointer in program + const RoundRectClipState* state = mSnapshot->roundRectClipState; + const Rect& innerRect = state->outlineInnerRect; + glUniform4f(mCaches.currentProgram->getUniform("roundRectInnerRectLTRB"), + innerRect.left, innerRect.top, + innerRect.right, innerRect.bottom); + glUniform1f(mCaches.currentProgram->getUniform("roundRectRadius"), + state->outlineRadius); + glUniformMatrix4fv(mCaches.currentProgram->getUniform("roundRectInvTransform"), + 1, GL_FALSE, &state->matrix.data[0]); + } } void OpenGLRenderer::setupDrawDirtyRegionsDisabled() { @@ -1694,12 +1706,14 @@ void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset, } bool dirty = right - left > 0.0f && bottom - top > 0.0f; - if (!ignoreTransform) { - mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, *currentTransform(), offset); - if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *currentTransform()); - } else { - mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, mat4::identity(), offset); - if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); + const Matrix4& transformMatrix = ignoreTransform ? Matrix4::identity() : *currentTransform(); + mCaches.currentProgram->set(mSnapshot->getOrthoMatrix(), mModelViewMatrix, transformMatrix, offset); + if (dirty && mTrackDirtyRegions) { + if (!ignoreTransform) { + dirtyLayer(left, top, right, bottom, *currentTransform()); + } else { + dirtyLayer(left, top, right, bottom); + } } } @@ -2912,7 +2926,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { bool clipRequired = false; const bool rejected = calculateQuickRejectForScissor(x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, false); + x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, NULL, false); if (rejected) { if (transform && !transform->isIdentity()) { @@ -3443,6 +3457,13 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, bool swapSrcDst) { + + if (mSnapshot->roundRectClipState != NULL /*&& !mSkipOutlineClip*/) { + blend = true; + mDescription.hasRoundRectClip = true; + } + mSkipOutlineClip = true; + if (mCountOverdraw) { if (!mCaches.blend) glEnable(GL_BLEND); if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 4f7f01e..f70ae58 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -254,8 +254,8 @@ public: return mSnapshot->clipRegion->isEmpty(); } - int getViewportWidth() { return currentSnapshot()->viewport.getWidth(); } - int getViewportHeight() { return currentSnapshot()->viewport.getHeight(); } + int getViewportWidth() { return currentSnapshot()->getViewportWidth(); } + int getViewportHeight() { return currentSnapshot()->getViewportHeight(); } /** * Scales the alpha on the current snapshot. This alpha value will be modulated @@ -354,12 +354,6 @@ public: protected: /** - * Computes the projection matrix, initialize the first snapshot - * and stores the dimensions of the render target. - */ - void initViewport(int width, int height); - - /** * Perform the setup specific to a frame. This method does not * issue any OpenGL commands. */ @@ -930,9 +924,6 @@ private: */ Texture* getTexture(const SkBitmap* bitmap); - // Ortho matrix used for projection in shaders - mat4 mProjectionMatrix; - /** * Model-view matrix used to position/size objects * @@ -1002,6 +993,8 @@ private: bool mCountOverdraw; float mOverdraw; + bool mSkipOutlineClip; + friend class DisplayListRenderer; friend class Layer; friend class TextSetupFunctor; diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h index 530be30..5c24335 100644 --- a/libs/hwui/Outline.h +++ b/libs/hwui/Outline.h @@ -58,11 +58,24 @@ public: mShouldClip = clip; } + bool getShouldClip() const { + return mShouldClip; + } + bool willClip() const { // only round rect outlines can be used for clipping return mShouldClip && (mType == kOutlineType_RoundRect); } + bool getAsRoundRect(Rect* outRect, float* outRadius) const { + if (mType == kOutlineType_RoundRect) { + outRect->set(mBounds); + *outRadius = mRadius; + return true; + } + return false; + } + const SkPath* getPath() const { if (mType == kOutlineType_None) return NULL; diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 33c91b3..3e191d0 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -45,17 +45,18 @@ namespace uirenderer { #define COLOR_COMPONENT_THRESHOLD 1.0f #define COLOR_COMPONENT_INV_THRESHOLD 0.0f -#define PROGRAM_KEY_TEXTURE 0x1 -#define PROGRAM_KEY_A8_TEXTURE 0x2 -#define PROGRAM_KEY_BITMAP 0x4 -#define PROGRAM_KEY_GRADIENT 0x8 -#define PROGRAM_KEY_BITMAP_FIRST 0x10 -#define PROGRAM_KEY_COLOR_MATRIX 0x20 -#define PROGRAM_KEY_COLOR_BLEND 0x40 -#define PROGRAM_KEY_BITMAP_NPOT 0x80 -#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 - -#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 +#define PROGRAM_KEY_TEXTURE 0x01 +#define PROGRAM_KEY_A8_TEXTURE 0x02 +#define PROGRAM_KEY_BITMAP 0x04 +#define PROGRAM_KEY_GRADIENT 0x08 +#define PROGRAM_KEY_BITMAP_FIRST 0x10 +#define PROGRAM_KEY_COLOR_MATRIX 0x20 +#define PROGRAM_KEY_COLOR_BLEND 0x40 +#define PROGRAM_KEY_BITMAP_NPOT 0x80 + +#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 + +#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 // Encode the xfermodes on 6 bits @@ -83,6 +84,7 @@ namespace uirenderer { #define PROGRAM_HAS_DEBUG_HIGHLIGHT 42 #define PROGRAM_EMULATE_STENCIL 43 +#define PROGRAM_HAS_ROUND_RECT_CLIP 44 /////////////////////////////////////////////////////////////////////////////// // Types @@ -158,6 +160,7 @@ struct ProgramDescription { bool hasDebugHighlight; bool emulateStencil; + bool hasRoundRectClip; /** * Resets this description. All fields are reset back to the default @@ -198,6 +201,8 @@ struct ProgramDescription { gamma = 2.2f; hasDebugHighlight = false; + emulateStencil = false; + hasRoundRectClip = false; } /** @@ -264,6 +269,7 @@ struct ProgramDescription { if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL; + if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP; return key; } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 6d50410..f451690 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -58,6 +58,8 @@ const char* gVS_Header_Uniforms_HasGradient = const char* gVS_Header_Uniforms_HasBitmap = "uniform mat4 textureTransform;\n" "uniform mediump vec2 textureDimension;\n"; +const char* gVS_Header_Uniforms_HasRoundRectClip = + "uniform mat4 roundRectInvTransform;\n"; const char* gVS_Header_Varyings_HasTexture = "varying vec2 outTexCoords;\n"; const char* gVS_Header_Varyings_HasColors = @@ -85,6 +87,8 @@ const char* gVS_Header_Varyings_HasGradient[6] = { "varying highp vec2 sweep;\n" "varying vec2 ditherTexCoords;\n", }; +const char* gVS_Header_Varyings_HasRoundRectClip = + "varying vec2 roundRectPos;\n"; const char* gVS_Main = "\nvoid main(void) {\n"; const char* gVS_Main_OutTexCoords = @@ -115,9 +119,12 @@ const char* gVS_Main_OutGradient[6] = { const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; const char* gVS_Main_Position = - " gl_Position = projection * transform * position;\n"; + " vec4 transformedPosition = projection * transform * position;\n" + " gl_Position = transformedPosition;\n"; const char* gVS_Main_AAVertexShape = " alpha = vtxAlpha;\n"; +const char* gVS_Main_HasRoundRectClip = + " roundRectPos = (roundRectInvTransform * transformedPosition).xy;\n"; const char* gVS_Footer = "}\n\n"; @@ -160,6 +167,10 @@ const char* gFS_Uniforms_ColorOp[3] = { const char* gFS_Uniforms_Gamma = "uniform float gamma;\n"; +const char* gFS_Uniforms_HasRoundRectClip = + "uniform vec4 roundRectInnerRectLTRB;\n" + "uniform float roundRectRadius;\n"; + const char* gFS_Main = "\nvoid main(void) {\n" " lowp vec4 fragColor;\n"; @@ -318,6 +329,15 @@ const char* gFS_Main_ApplyColorOp[3] = { // PorterDuff " fragColor = blendColors(colorBlend, fragColor);\n" }; + +// Note: LTRB -> xyzw +const char* gFS_Main_FragColor_HasRoundRectClip = + " mediump vec2 fragToLT = roundRectInnerRectLTRB.xy - roundRectPos;\n" + " mediump vec2 fragFromRB = roundRectPos - roundRectInnerRectLTRB.zw;\n" + " mediump vec2 dist = max(max(fragToLT, fragFromRB), vec2(0.0, 0.0));\n" + " mediump float linearDist = roundRectRadius - length(dist);\n" + " gl_FragColor *= clamp(linearDist, 0.0, 1.0);\n"; + const char* gFS_Main_DebugHighlight = " gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n"; const char* gFS_Main_EmulateStencil = @@ -462,6 +482,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasBitmap) { shader.append(gVS_Header_Uniforms_HasBitmap); } + if (description.hasRoundRectClip) { + shader.append(gVS_Header_Uniforms_HasRoundRectClip); + } // Varyings if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Varyings_HasTexture); @@ -478,6 +501,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasBitmap) { shader.append(gVS_Header_Varyings_HasBitmap); } + if (description.hasRoundRectClip) { + shader.append(gVS_Header_Varyings_HasRoundRectClip); + } // Begin the shader shader.append(gVS_Main); { @@ -500,6 +526,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasGradient) { shader.append(gVS_Main_OutGradient[gradientIndex(description)]); } + if (description.hasRoundRectClip) { + shader.append(gVS_Main_HasRoundRectClip); + } } // End the shader shader.append(gVS_Footer); @@ -546,6 +575,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasBitmap) { shader.append(gVS_Header_Varyings_HasBitmap); } + if (description.hasRoundRectClip) { + shader.append(gVS_Header_Varyings_HasRoundRectClip); + } // Uniforms int modulateOp = MODULATE_OP_NO_MODULATE; @@ -568,11 +600,18 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasGammaCorrection) { shader.append(gFS_Uniforms_Gamma); } + if (description.hasRoundRectClip) { + shader.append(gFS_Uniforms_HasRoundRectClip); + } // Optimization for common cases - if (!description.isAA && !blendFramebuffer && !description.hasColors && - description.colorOp == ProgramDescription::kColorNone && - !description.hasDebugHighlight && !description.emulateStencil) { + if (!description.isAA + && !blendFramebuffer + && !description.hasColors + && description.colorOp == ProgramDescription::kColorNone + && !description.hasDebugHighlight + && !description.emulateStencil + && !description.hasRoundRectClip) { bool fast = false; const bool noShader = !description.hasGradient && !description.hasBitmap; @@ -722,6 +761,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasColors) { shader.append(gFS_Main_FragColor_HasColors); } + if (description.hasRoundRectClip) { + shader.append(gFS_Main_FragColor_HasRoundRectClip); + } if (description.hasDebugHighlight) { shader.append(gFS_Main_DebugHighlight); } diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index f38d8b7..2ddbbd7 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -234,7 +234,7 @@ public: bottom = ceilf(bottom); } - void dump(const char* label) const { + void dump(const char* label = NULL) const { ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom); } diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 9902ff1..f0645a9 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -53,7 +53,7 @@ void RenderNode::outputLogBuffer(int fd) { } RenderNode::RenderNode() - : mNeedsPropertiesSync(false) + : mDirtyPropertyFields(0) , mNeedsDisplayListDataSync(false) , mDisplayListData(0) , mStagingDisplayListData(0) @@ -109,23 +109,37 @@ void RenderNode::prepareTreeImpl(TreeInfo& info) { prepareSubTree(info, mDisplayListData); } -static bool is_finished(const sp<BaseRenderNodeAnimator>& animator) { - return animator->isFinished(); -} +class PushAnimatorsFunctor { +public: + PushAnimatorsFunctor(RenderNode* target, TreeInfo& info) + : mTarget(target), mInfo(info) {} -void RenderNode::pushStagingChanges(TreeInfo& info) { - if (mNeedsPropertiesSync) { - mNeedsPropertiesSync = false; - mProperties = mStagingProperties; + bool operator() (const sp<BaseRenderNodeAnimator>& animator) { + animator->setupStartValueIfNecessary(mTarget, mInfo); + return animator->isFinished(); } +private: + RenderNode* mTarget; + TreeInfo& mInfo; +}; + +void RenderNode::pushStagingChanges(TreeInfo& info) { + // Push the animators first so that setupStartValueIfNecessary() is called + // before properties() is trampled by stagingProperties(), as they are + // required by some animators. if (mNeedsAnimatorsSync) { mAnimators.resize(mStagingAnimators.size()); std::vector< sp<BaseRenderNodeAnimator> >::iterator it; + PushAnimatorsFunctor functor(this, info); // hint: this means copy_if_not() it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(), - mAnimators.begin(), is_finished); + mAnimators.begin(), functor); mAnimators.resize(std::distance(mAnimators.begin(), it)); } + if (mDirtyPropertyFields) { + mDirtyPropertyFields = 0; + mProperties = mStagingProperties; + } if (mNeedsDisplayListDataSync) { mNeedsDisplayListDataSync = false; // Do a push pass on the old tree to handle freeing DisplayListData @@ -144,7 +158,7 @@ public: AnimateFunctor(RenderNode* target, TreeInfo& info) : mTarget(target), mInfo(info) {} - bool operator() (sp<BaseRenderNodeAnimator>& animator) { + bool operator() (const sp<BaseRenderNodeAnimator>& animator) { return animator->animate(mTarget, mInfo); } private: @@ -238,9 +252,8 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { } if (CC_UNLIKELY(properties().hasClippingPath())) { - // TODO: optimize for round rect/circle clipping - const SkPath* path = properties().getClippingPath(); - ClipPathOp* op = new (handler.allocator()) ClipPathOp(path, SkRegion::kIntersect_Op); + ClipPathOp* op = new (handler.allocator()) ClipPathOp( + properties().getClippingPath(), properties().getClippingPathOp()); handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); } } @@ -654,6 +667,10 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { bool quickRejected = properties().getClipToBounds() && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); if (!quickRejected) { + if (mProperties.getOutline().willClip()) { + renderer.setClippingOutline(alloc, &(mProperties.getOutline())); + } + Vector<ZDrawDisplayListOpPair> zTranslatedNodes; buildZSortedChildList(zTranslatedNodes); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index bc62ee1..1811a7b 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -82,6 +82,22 @@ class DrawDisplayListOp; */ class RenderNode : public VirtualLightRefBase { public: + enum DirtyPropertyMask { + GENERIC = 1 << 1, + TRANSLATION_X = 1 << 2, + TRANSLATION_Y = 1 << 3, + TRANSLATION_Z = 1 << 4, + SCALE_X = 1 << 5, + SCALE_Y = 1 << 6, + ROTATION = 1 << 7, + ROTATION_X = 1 << 8, + ROTATION_Y = 1 << 9, + X = 1 << 10, + Y = 1 << 11, + Z = 1 << 12, + ALPHA = 1 << 13, + }; + ANDROID_API RenderNode(); ANDROID_API virtual ~RenderNode(); @@ -123,6 +139,14 @@ public: } } + bool isPropertyFieldDirty(DirtyPropertyMask field) const { + return mDirtyPropertyFields & field; + } + + void setPropertyFieldsDirty(uint32_t fields) { + mDirtyPropertyFields |= fields; + } + const RenderProperties& properties() { return mProperties; } @@ -136,7 +160,6 @@ public: } RenderProperties& mutateStagingProperties() { - mNeedsPropertiesSync = true; return mStagingProperties; } @@ -152,6 +175,7 @@ public: // UI thread only! ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator) { + animator->onAttached(this); mStagingAnimators.insert(animator); mNeedsAnimatorsSync = true; } @@ -227,7 +251,7 @@ private: String8 mName; - bool mNeedsPropertiesSync; + uint32_t mDirtyPropertyFields; RenderProperties mProperties; RenderProperties mStagingProperties; diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 99de1fc..5f7d4e3 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -50,14 +50,11 @@ RenderProperties::PrimitiveFields::PrimitiveFields() } RenderProperties::ComputedFields::ComputedFields() - : mTransformMatrix(NULL) - , mClipPath(NULL) - , mClipPathOp(SkRegion::kIntersect_Op) { + : mTransformMatrix(NULL) { } RenderProperties::ComputedFields::~ComputedFields() { delete mTransformMatrix; - delete mClipPath; } RenderProperties::RenderProperties() @@ -77,9 +74,6 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) { setAnimationMatrix(other.getAnimationMatrix()); setCameraDistance(other.getCameraDistance()); - // Update the computed clip path - updateClipPath(); - // Force recalculation of the matrix, since other's dirty bit may be clear mPrimitiveFields.mMatrixOrPivotDirty = true; updateMatrix(); @@ -166,39 +160,5 @@ void RenderProperties::updateMatrix() { } } -void RenderProperties::updateClipPath() { - const SkPath* outlineClipPath = mPrimitiveFields.mOutline.willClip() - ? mPrimitiveFields.mOutline.getPath() : NULL; - const SkPath* revealClipPath = mPrimitiveFields.mRevealClip.getPath(); - - if (!outlineClipPath && !revealClipPath) { - // mComputedFields.mClipPath doesn't need to be updated, since it won't be used - return; - } - - if (mComputedFields.mClipPath == NULL) { - mComputedFields.mClipPath = new SkPath(); - } - SkPath* clipPath = mComputedFields.mClipPath; - mComputedFields.mClipPathOp = SkRegion::kIntersect_Op; - - if (outlineClipPath && revealClipPath) { - SkPathOp op = kIntersect_PathOp; - if (mPrimitiveFields.mRevealClip.isInverseClip()) { - op = kDifference_PathOp; // apply difference step in the Op below, instead of draw time - } - - Op(*outlineClipPath, *revealClipPath, op, clipPath); - } else if (outlineClipPath) { - *clipPath = *outlineClipPath; - } else { - *clipPath = *revealClipPath; - if (mPrimitiveFields.mRevealClip.isInverseClip()) { - // apply difference step at draw time - mComputedFields.mClipPathOp = SkRegion::kDifference_Op; - } - } -} - } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 6fc8bce..c0e3ce7 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -437,19 +437,17 @@ public: ANDROID_API void updateMatrix(); - ANDROID_API void updateClipPath(); - - // signals that mComputedFields.mClipPath is up to date, and should be used for clipping bool hasClippingPath() const { - return mPrimitiveFields.mOutline.willClip() || mPrimitiveFields.mRevealClip.willClip(); + return mPrimitiveFields.mRevealClip.willClip(); } const SkPath* getClippingPath() const { - return hasClippingPath() ? mComputedFields.mClipPath : NULL; + return mPrimitiveFields.mRevealClip.getPath(); } SkRegion::Op getClippingPathOp() const { - return mComputedFields.mClipPathOp; + return mPrimitiveFields.mRevealClip.isInverseClip() + ? SkRegion::kDifference_Op : SkRegion::kIntersect_Op; } Outline& mutableOutline() { @@ -505,8 +503,6 @@ private: SkMatrix* mTransformMatrix; Sk3DView mTransformCamera; - SkPath* mClipPath; // TODO: remove this, create new ops for efficient/special case clipping - SkRegion::Op mClipPathOp; } mComputedFields; }; diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index be49aed..55b82e4 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -196,6 +196,10 @@ Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { * @param len the number of points of the polygon */ bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) { + if (len < 2 || polygon == NULL) { + ALOGW("Invalid polygon %p, length is %d @ isClockwise()", polygon, len); + return true; + } double sum = 0; double p1x = polygon[len - 1].x; double p1y = polygon[len - 1].y; diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index 6bfa203..80f7eca 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -20,6 +20,8 @@ #include <SkCanvas.h> +#include "utils/MathUtils.h" + namespace android { namespace uirenderer { @@ -34,8 +36,8 @@ Snapshot::Snapshot() , fbo(0) , invisible(false) , empty(false) - , height(0) - , alpha(1.0f) { + , alpha(1.0f) + , roundRectClipState(NULL) { transform = &mTransformRoot; clipRect = &mClipRectRoot; region = NULL; @@ -53,10 +55,9 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) , fbo(s->fbo) , invisible(s->invisible) , empty(false) - , viewport(s->viewport) - , height(s->height) - , alpha(s->alpha) { - + , alpha(s->alpha) + , roundRectClipState(s->roundRectClipState) + , mViewportData(s->mViewportData) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); transform = &mTransformRoot; @@ -206,6 +207,49 @@ void Snapshot::resetTransform(float x, float y, float z) { } /////////////////////////////////////////////////////////////////////////////// +// Clipping outline +/////////////////////////////////////////////////////////////////////////////// + +void Snapshot::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { + Rect bounds; + float radius; + if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported + + if (!MathUtils::isPositive(radius)) return; // leave clipping up to rect clipping + + RoundRectClipState* state = new (allocator) RoundRectClipState; + + // store the inverse drawing matrix + Matrix4 outlineDrawingMatrix; + outlineDrawingMatrix.load(getOrthoMatrix()); + outlineDrawingMatrix.multiply(*transform); + state->matrix.loadInverse(outlineDrawingMatrix); + + // compute area under rounded corners - only draws overlapping these rects need to be clipped + for (int i = 0 ; i < 4; i++) { + state->dangerRects[i] = bounds; + } + state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius; + state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius; + state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius; + state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius; + for (int i = 0; i < 4; i++) { + transform->mapRect(state->dangerRects[i]); + + // round danger rects out as though they are AA geometry (since they essentially are) + state->dangerRects[i].snapGeometryToPixelBoundaries(true); + } + + // store RR area + bounds.inset(radius); + state->outlineInnerRect = bounds; + state->outlineRadius = radius; + + // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info + roundRectClipState = state; +} + +/////////////////////////////////////////////////////////////////////////////// // Queries /////////////////////////////////////////////////////////////////////////////// @@ -215,7 +259,7 @@ bool Snapshot::isIgnored() const { void Snapshot::dump() const { ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d", - this, flags, previous.get(), height, isIgnored(), clipRegion && !clipRegion->isEmpty()); + this, flags, previous.get(), getViewportHeight(), isIgnored(), clipRegion && !clipRegion->isEmpty()); ALOGD(" ClipRect (at %p) %.1f %.1f %.1f %.1f", clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); ALOGD(" Transform (at %p):", transform); diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 038aea8..435736c 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -20,6 +20,7 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <utils/LinearAllocator.h> #include <utils/RefBase.h> #include <ui/Region.h> @@ -27,12 +28,40 @@ #include "Layer.h" #include "Matrix.h" +#include "Outline.h" #include "Rect.h" +#include "utils/Macros.h" namespace android { namespace uirenderer { /** + * Temporary structure holding information for a single outline clip. + * + * These structures are treated as immutable once created, and only exist for a single frame, which + * is why they may only be allocated with a LinearAllocator. + */ +class RoundRectClipState { +public: + /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/ + static void* operator new(size_t size, LinearAllocator& allocator) { + return allocator.alloc(size); + } + + bool areaRequiresRoundRectClip(const Rect& rect) const { + return rect.intersects(dangerRects[0]) + || rect.intersects(dangerRects[1]) + || rect.intersects(dangerRects[2]) + || rect.intersects(dangerRects[3]); + } + + Matrix4 matrix; + Rect dangerRects[4]; + Rect outlineInnerRect; + float outlineRadius; +}; + +/** * A snapshot holds information about the current state of the rendering * surface. A snapshot is usually created whenever the user calls save() * and discarded when the user calls restore(). Once a snapshot is created, @@ -65,17 +94,16 @@ public: * Indicates that this snapshot is a special type of layer * backed by an FBO. This flag only makes sense when the * flag kFlagIsLayer is also set. + * + * Viewport has been modified to fit the new Fbo, and must be + * restored when this snapshot is restored. */ kFlagIsFboLayer = 0x4, /** - * Indicates that this snapshot has changed the ortho matrix. - */ - kFlagDirtyOrtho = 0x8, - /** * Indicates that this snapshot or an ancestor snapshot is * an FBO layer. */ - kFlagFboTarget = 0x10 + kFlagFboTarget = 0x8, }; /** @@ -125,6 +153,19 @@ public: */ void resetTransform(float x, float y, float z); + void initializeViewport(int width, int height) { + mViewportData.initialize(width, height); + } + + int getViewportWidth() const { return mViewportData.mWidth; } + int getViewportHeight() const { return mViewportData.mHeight; } + const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; } + + /** + * Sets (and replaces) the current clipping outline + */ + void setClippingOutline(LinearAllocator& allocator, const Outline* outline); + /** * Indicates whether this snapshot should be ignored. A snapshot * is typicalled ignored if its layer is invisible or empty. @@ -173,21 +214,6 @@ public: bool empty; /** - * Current viewport. - */ - Rect viewport; - - /** - * Height of the framebuffer the snapshot is rendering into. - */ - int height; - - /** - * Contains the previous ortho matrix. - */ - mat4 orthoMatrix; - - /** * Local transformation. Holds the current translation, scale and * rotation values. * @@ -233,9 +259,38 @@ public: */ float alpha; + /** + * Current clipping round rect. + * + * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips, + * never modified. + */ + const RoundRectClipState* roundRectClipState; + void dump() const; private: + struct ViewportData { + ViewportData() : mWidth(0), mHeight() {} + void initialize(int width, int height) { + mWidth = width; + mHeight = height; + mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + } + + /* + * Width and height of current viewport. + * + * The viewport is always defined to be (0, 0, width, height). + */ + int mWidth; + int mHeight; + /** + * Contains the current orthographic, projection matrix. + */ + mat4 mOrthoMatrix; + }; + void ensureClipRegion(); void copyClipRectFromRegion(); @@ -246,6 +301,7 @@ private: Rect mLocalClip; // don't use directly, call getLocalClip() which initializes this SkRegion mClipRegionRoot; + ViewportData mViewportData; }; // class Snapshot diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp index 05f6cf8..7d299f0 100644 --- a/libs/hwui/StatefulBaseRenderer.cpp +++ b/libs/hwui/StatefulBaseRenderer.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "OpenGLRenderer" + #include <SkCanvas.h> #include "StatefulBaseRenderer.h" @@ -38,9 +40,7 @@ void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop, void StatefulBaseRenderer::initializeViewport(int width, int height) { mWidth = width; mHeight = height; - - mFirstSnapshot->height = height; - mFirstSnapshot->viewport.set(0, 0, width, height); + mFirstSnapshot->initializeViewport(width, height); } /////////////////////////////////////////////////////////////////////////////// @@ -182,6 +182,10 @@ bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { return !mSnapshot->clipRect->isEmpty(); } +void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { + mSnapshot->setClippingOutline(allocator, outline); +} + /////////////////////////////////////////////////////////////////////////////// // Quick Rejection /////////////////////////////////////////////////////////////////////////////// @@ -197,7 +201,9 @@ bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { * See Rect::snapGeometryToPixelBoundaries() */ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, - float right, float bottom, bool* clipRequired, bool snapOut) const { + float right, float bottom, + bool* clipRequired, bool* roundRectClipRequired, + bool snapOut) const { if (mSnapshot->isIgnored() || bottom <= top || right <= left) { return true; } @@ -212,7 +218,15 @@ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, if (!clipRect.intersects(r)) return true; // clip is required if geometry intersects clip rect - if (clipRequired) *clipRequired = !clipRect.contains(r); + if (clipRequired) { + *clipRequired = !clipRect.contains(r); + } + + // round rect clip is required if RR clip exists, and geometry intersects its corners + if (roundRectClipRequired) { + *roundRectClipRequired = mSnapshot->roundRectClipState != NULL + && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); + } return false; } diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h index 64354ac..2e7f279 100644 --- a/libs/hwui/StatefulBaseRenderer.h +++ b/libs/hwui/StatefulBaseRenderer.h @@ -46,6 +46,11 @@ public: virtual status_t prepare(bool opaque) { return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque); } + + /** + * Initialize the first snapshot, computing the projection matrix, + * and stores the dimensions of the render target. + */ void initializeViewport(int width, int height); void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom); @@ -83,6 +88,14 @@ public: virtual bool clipPath(const SkPath* path, SkRegion::Op op); virtual bool clipRegion(const SkRegion* region, SkRegion::Op op); + /** + * Does not support different clipping Ops (that is, every call to setClippingOutline is + * effectively using SkRegion::kReplaceOp) + * + * The clipping outline is independent from the regular clip. + */ + void setClippingOutline(LinearAllocator& allocator, const Outline* outline); + protected: const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); } @@ -101,7 +114,7 @@ protected: // Clip bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, - bool* clipRequired, bool snapOut) const; + bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const; /** * Called just after a restore has occurred. The 'removed' snapshot popped from the stack, diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index fc5994c..d4a23b8 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -21,12 +21,12 @@ namespace android { namespace uirenderer { -class BaseAnimator; +class BaseRenderNodeAnimator; class AnimationListener; class AnimationHook { public: - virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) = 0; + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) = 0; protected: ~AnimationHook() {} }; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5754536..48cd8fc 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -31,11 +31,10 @@ #define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" #define GLES_VERSION 2 +#define USE_TEXTURE_ATLAS false -#ifdef USE_OPENGL_RENDERER // Android-specific addition that is used to show when frames began in systrace EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); -#endif namespace android { namespace uirenderer { @@ -230,7 +229,9 @@ void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, } void GlobalContext::initAtlas() { - Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + if (USE_TEXTURE_ATLAS) { + Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + } } void GlobalContext::usePBufferSurface() { diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 1a7082b..997acde2 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -34,6 +34,10 @@ public: return value >= gNonZeroEpsilon; } + inline static bool areEqual(float valueA, float valueB) { + return isZero(valueA - valueB); + } + inline static int min(int a, int b) { return a < b ? a : b; } diff --git a/media/java/android/media/TtmlRenderer.java b/media/java/android/media/TtmlRenderer.java new file mode 100644 index 0000000..0309334 --- /dev/null +++ b/media/java/android/media/TtmlRenderer.java @@ -0,0 +1,751 @@ +/* + * 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.media; + +import android.content.Context; +import android.graphics.Color; +import android.media.SubtitleTrack.RenderingWidget.OnChangedListener; +import android.text.Layout.Alignment; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.MeasureSpec; +import android.view.ViewGroup.LayoutParams; +import android.view.accessibility.CaptioningManager; +import android.view.accessibility.CaptioningManager.CaptionStyle; +import android.view.accessibility.CaptioningManager.CaptioningChangeListener; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.widget.SubtitleView; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.TreeSet; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +/** @hide */ +public class TtmlRenderer extends SubtitleController.Renderer { + private final Context mContext; + + private static final String MEDIA_MIMETYPE_TEXT_TTML = "application/ttml+xml"; + + private TtmlRenderingWidget mRenderingWidget; + + public TtmlRenderer(Context context) { + mContext = context; + } + + @Override + public boolean supports(MediaFormat format) { + if (format.containsKey(MediaFormat.KEY_MIME)) { + return format.getString(MediaFormat.KEY_MIME).equals(MEDIA_MIMETYPE_TEXT_TTML); + } + return false; + } + + @Override + public SubtitleTrack createTrack(MediaFormat format) { + if (mRenderingWidget == null) { + mRenderingWidget = new TtmlRenderingWidget(mContext); + } + return new TtmlTrack(mRenderingWidget, format); + } +} + +/** + * A class which provides utillity methods for TTML parsing. + * + * @hide + */ +final class TtmlUtils { + public static final String TAG_TT = "tt"; + public static final String TAG_HEAD = "head"; + public static final String TAG_BODY = "body"; + public static final String TAG_DIV = "div"; + public static final String TAG_P = "p"; + public static final String TAG_SPAN = "span"; + public static final String TAG_BR = "br"; + public static final String TAG_STYLE = "style"; + public static final String TAG_STYLING = "styling"; + public static final String TAG_LAYOUT = "layout"; + public static final String TAG_REGION = "region"; + public static final String TAG_METADATA = "metadata"; + public static final String TAG_SMPTE_IMAGE = "smpte:image"; + public static final String TAG_SMPTE_DATA = "smpte:data"; + public static final String TAG_SMPTE_INFORMATION = "smpte:information"; + public static final String PCDATA = "#pcdata"; + public static final String ATTR_BEGIN = "begin"; + public static final String ATTR_DURATION = "dur"; + public static final String ATTR_END = "end"; + public static final long INVALID_TIMESTAMP = Long.MAX_VALUE; + + /** + * Time expression RE according to the spec: + * http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression + */ + private static final Pattern CLOCK_TIME = Pattern.compile( + "^([0-9][0-9]+):([0-9][0-9]):([0-9][0-9])" + + "(?:(\\.[0-9]+)|:([0-9][0-9])(?:\\.([0-9]+))?)?$"); + + private static final Pattern OFFSET_TIME = Pattern.compile( + "^([0-9]+(?:\\.[0-9]+)?)(h|m|s|ms|f|t)$"); + + private TtmlUtils() { + } + + /** + * Parses the given time expression and returns a timestamp in millisecond. + * <p> + * For the format of the time expression, please refer <a href= + * "http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a> + * + * @param time A string which includes time expression. + * @param frameRate the framerate of the stream. + * @param subframeRate the sub-framerate of the stream + * @param tickRate the tick rate of the stream. + * @return the parsed timestamp in micro-second. + * @throws NumberFormatException if the given string does not match to the + * format. + */ + public static long parseTimeExpression(String time, int frameRate, int subframeRate, + int tickRate) throws NumberFormatException { + Matcher matcher = CLOCK_TIME.matcher(time); + if (matcher.matches()) { + String hours = matcher.group(1); + double durationSeconds = Long.parseLong(hours) * 3600; + String minutes = matcher.group(2); + durationSeconds += Long.parseLong(minutes) * 60; + String seconds = matcher.group(3); + durationSeconds += Long.parseLong(seconds); + String fraction = matcher.group(4); + durationSeconds += (fraction != null) ? Double.parseDouble(fraction) : 0; + String frames = matcher.group(5); + durationSeconds += (frames != null) ? ((double)Long.parseLong(frames)) / frameRate : 0; + String subframes = matcher.group(6); + durationSeconds += (subframes != null) ? ((double)Long.parseLong(subframes)) + / subframeRate / frameRate + : 0; + return (long)(durationSeconds * 1000); + } + matcher = OFFSET_TIME.matcher(time); + if (matcher.matches()) { + String timeValue = matcher.group(1); + double value = Double.parseDouble(timeValue); + String unit = matcher.group(2); + if (unit.equals("h")) { + value *= 3600L * 1000000L; + } else if (unit.equals("m")) { + value *= 60 * 1000000; + } else if (unit.equals("s")) { + value *= 1000000; + } else if (unit.equals("ms")) { + value *= 1000; + } else if (unit.equals("f")) { + value = value / frameRate * 1000000; + } else if (unit.equals("t")) { + value = value / tickRate * 1000000; + } + return (long)value; + } + throw new NumberFormatException("Malformed time expression : " + time); + } + + /** + * Applies <a href + * src="http://www.w3.org/TR/ttaf1-dfxp/#content-attribute-space">the + * default space policy</a> to the given string. + * + * @param in A string to apply the policy. + */ + public static String applyDefaultSpacePolicy(String in) { + return applySpacePolicy(in, true); + } + + /** + * Applies the space policy to the given string. This applies <a href + * src="http://www.w3.org/TR/ttaf1-dfxp/#content-attribute-space">the + * default space policy</a> with linefeed-treatment as treat-as-space + * or preserve. + * + * @param in A string to apply the policy. + * @param treatLfAsSpace Whether convert line feeds to spaces or not. + */ + public static String applySpacePolicy(String in, boolean treatLfAsSpace) { + // Removes CR followed by LF. ref: + // http://www.w3.org/TR/xml/#sec-line-ends + String crRemoved = in.replaceAll("\r\n", "\n"); + // Apply suppress-at-line-break="auto" and + // white-space-treatment="ignore-if-surrounding-linefeed" + String spacesNeighboringLfRemoved = crRemoved.replaceAll(" *\n *", "\n"); + // Apply linefeed-treatment="treat-as-space" + String lfToSpace = treatLfAsSpace ? spacesNeighboringLfRemoved.replaceAll("\n", " ") + : spacesNeighboringLfRemoved; + // Apply white-space-collapse="true" + String spacesCollapsed = lfToSpace.replaceAll("[ \t\\x0B\f\r]+", " "); + return spacesCollapsed; + } + + /** + * Returns the timed text for the given time period. + * + * @param root The root node of the TTML document. + * @param startUs The start time of the time period in microsecond. + * @param endUs The end time of the time period in microsecond. + */ + public static String extractText(TtmlNode root, long startUs, long endUs) { + StringBuilder text = new StringBuilder(); + extractText(root, startUs, endUs, text, false); + return text.toString().replaceAll("\n$", ""); + } + + private static void extractText(TtmlNode node, long startUs, long endUs, StringBuilder out, + boolean inPTag) { + if (node.mName.equals(TtmlUtils.PCDATA) && inPTag) { + out.append(node.mText); + } else if (node.mName.equals(TtmlUtils.TAG_BR) && inPTag) { + out.append("\n"); + } else if (node.mName.equals(TtmlUtils.TAG_METADATA)) { + // do nothing. + } else if (node.isActive(startUs, endUs)) { + boolean pTag = node.mName.equals(TtmlUtils.TAG_P); + int length = out.length(); + for (int i = 0; i < node.mChildren.size(); ++i) { + extractText(node.mChildren.get(i), startUs, endUs, out, pTag || inPTag); + } + if (pTag && length != out.length()) { + out.append("\n"); + } + } + } + + /** + * Returns a TTML fragment string for the given time period. + * + * @param root The root node of the TTML document. + * @param startUs The start time of the time period in microsecond. + * @param endUs The end time of the time period in microsecond. + */ + public static String extractTtmlFragment(TtmlNode root, long startUs, long endUs) { + StringBuilder fragment = new StringBuilder(); + extractTtmlFragment(root, startUs, endUs, fragment); + return fragment.toString(); + } + + private static void extractTtmlFragment(TtmlNode node, long startUs, long endUs, + StringBuilder out) { + if (node.mName.equals(TtmlUtils.PCDATA)) { + out.append(node.mText); + } else if (node.mName.equals(TtmlUtils.TAG_BR)) { + out.append("<br/>"); + } else if (node.isActive(startUs, endUs)) { + out.append("<"); + out.append(node.mName); + out.append(node.mAttributes); + out.append(">"); + for (int i = 0; i < node.mChildren.size(); ++i) { + extractTtmlFragment(node.mChildren.get(i), startUs, endUs, out); + } + out.append("</"); + out.append(node.mName); + out.append(">"); + } + } +} + +/** + * A container class which represents a cue in TTML. + * @hide + */ +class TtmlCue extends SubtitleTrack.Cue { + public String mText; + public String mTtmlFragment; + + public TtmlCue(long startTimeMs, long endTimeMs, String text, String ttmlFragment) { + this.mStartTimeMs = startTimeMs; + this.mEndTimeMs = endTimeMs; + this.mText = text; + this.mTtmlFragment = ttmlFragment; + } +} + +/** + * A container class which represents a node in TTML. + * + * @hide + */ +class TtmlNode { + public final String mName; + public final String mAttributes; + public final TtmlNode mParent; + public final String mText; + public final List<TtmlNode> mChildren = new ArrayList<TtmlNode>(); + public final long mRunId; + public final long mStartTimeMs; + public final long mEndTimeMs; + + public TtmlNode(String name, String attributes, String text, long startTimeMs, long endTimeMs, + TtmlNode parent, long runId) { + this.mName = name; + this.mAttributes = attributes; + this.mText = text; + this.mStartTimeMs = startTimeMs; + this.mEndTimeMs = endTimeMs; + this.mParent = parent; + this.mRunId = runId; + } + + /** + * Check if this node is active in the given time range. + * + * @param startTimeMs The start time of the range to check in microsecond. + * @param endTimeMs The end time of the range to check in microsecond. + * @return return true if the given range overlaps the time range of this + * node. + */ + public boolean isActive(long startTimeMs, long endTimeMs) { + return this.mEndTimeMs > startTimeMs && this.mStartTimeMs < endTimeMs; + } +} + +/** + * A simple TTML parser (http://www.w3.org/TR/ttaf1-dfxp/) which supports DFXP + * presentation profile. + * <p> + * Supported features in this parser are: + * <ul> + * <li>content + * <li>core + * <li>presentation + * <li>profile + * <li>structure + * <li>time-offset + * <li>timing + * <li>tickRate + * <li>time-clock-with-frames + * <li>time-clock + * <li>time-offset-with-frames + * <li>time-offset-with-ticks + * </ul> + * </p> + * + * @hide + */ +class TtmlParser { + static final String TAG = "TtmlParser"; + + // TODO: read and apply the following attributes if specified. + private static final int DEFAULT_FRAMERATE = 30; + private static final int DEFAULT_SUBFRAMERATE = 1; + private static final int DEFAULT_TICKRATE = 1; + + private XmlPullParser mParser; + private final TtmlNodeListener mListener; + private long mCurrentRunId; + + public TtmlParser(TtmlNodeListener listener) { + mListener = listener; + } + + /** + * Parse TTML data. Once this is called, all the previous data are + * reset and it starts parsing for the given text. + * + * @param ttmlText TTML text to parse. + * @throws XmlPullParserException + * @throws IOException + */ + public void parse(String ttmlText, long runId) throws XmlPullParserException, IOException { + mParser = null; + mCurrentRunId = runId; + loadParser(ttmlText); + parseTtml(); + } + + private void loadParser(String ttmlFragment) throws XmlPullParserException { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + factory.setNamespaceAware(false); + mParser = factory.newPullParser(); + StringReader in = new StringReader(ttmlFragment); + mParser.setInput(in); + } + + private void extractAttribute(XmlPullParser parser, int i, StringBuilder out) { + out.append(" "); + out.append(parser.getAttributeName(i)); + out.append("=\""); + out.append(parser.getAttributeValue(i)); + out.append("\""); + } + + private void parseTtml() throws XmlPullParserException, IOException { + LinkedList<TtmlNode> nodeStack = new LinkedList<TtmlNode>(); + int depthInUnsupportedTag = 0; + boolean active = true; + while (!isEndOfDoc()) { + int eventType = mParser.getEventType(); + TtmlNode parent = nodeStack.peekLast(); + if (active) { + if (eventType == XmlPullParser.START_TAG) { + if (!isSupportedTag(mParser.getName())) { + Log.w(TAG, "Unsupported tag " + mParser.getName() + " is ignored."); + depthInUnsupportedTag++; + active = false; + } else { + TtmlNode node = parseNode(parent); + nodeStack.addLast(node); + if (parent != null) { + parent.mChildren.add(node); + } + } + } else if (eventType == XmlPullParser.TEXT) { + String text = TtmlUtils.applyDefaultSpacePolicy(mParser.getText()); + if (!TextUtils.isEmpty(text)) { + parent.mChildren.add(new TtmlNode( + TtmlUtils.PCDATA, "", text, 0, TtmlUtils.INVALID_TIMESTAMP, + parent, mCurrentRunId)); + + } + } else if (eventType == XmlPullParser.END_TAG) { + if (mParser.getName().equals(TtmlUtils.TAG_P)) { + mListener.onTtmlNodeParsed(nodeStack.getLast()); + } else if (mParser.getName().equals(TtmlUtils.TAG_TT)) { + mListener.onRootNodeParsed(nodeStack.getLast()); + } + nodeStack.removeLast(); + } + } else { + if (eventType == XmlPullParser.START_TAG) { + depthInUnsupportedTag++; + } else if (eventType == XmlPullParser.END_TAG) { + depthInUnsupportedTag--; + if (depthInUnsupportedTag == 0) { + active = true; + } + } + } + mParser.next(); + } + } + + private TtmlNode parseNode(TtmlNode parent) throws XmlPullParserException, IOException { + int eventType = mParser.getEventType(); + if (!(eventType == XmlPullParser.START_TAG)) { + return null; + } + StringBuilder attrStr = new StringBuilder(); + long start = 0; + long end = TtmlUtils.INVALID_TIMESTAMP; + long dur = 0; + for (int i = 0; i < mParser.getAttributeCount(); ++i) { + String attr = mParser.getAttributeName(i); + String value = mParser.getAttributeValue(i); + // TODO: check if it's safe to ignore the namespace of attributes as follows. + attr = attr.replaceFirst("^.*:", ""); + if (attr.equals(TtmlUtils.ATTR_BEGIN)) { + start = TtmlUtils.parseTimeExpression(value, DEFAULT_FRAMERATE, + DEFAULT_SUBFRAMERATE, DEFAULT_TICKRATE); + } else if (attr.equals(TtmlUtils.ATTR_END)) { + end = TtmlUtils.parseTimeExpression(value, DEFAULT_FRAMERATE, DEFAULT_SUBFRAMERATE, + DEFAULT_TICKRATE); + } else if (attr.equals(TtmlUtils.ATTR_DURATION)) { + dur = TtmlUtils.parseTimeExpression(value, DEFAULT_FRAMERATE, DEFAULT_SUBFRAMERATE, + DEFAULT_TICKRATE); + } else { + extractAttribute(mParser, i, attrStr); + } + } + if (parent != null) { + start += parent.mStartTimeMs; + if (end != TtmlUtils.INVALID_TIMESTAMP) { + end += parent.mStartTimeMs; + } + } + if (dur > 0) { + if (end != TtmlUtils.INVALID_TIMESTAMP) { + Log.e(TAG, "'dur' and 'end' attributes are defined at the same time." + + "'end' value is ignored."); + } + end = start + dur; + } + if (parent != null) { + // If the end time remains unspecified, then the end point is + // interpreted as the end point of the external time interval. + if (end == TtmlUtils.INVALID_TIMESTAMP && + parent.mEndTimeMs != TtmlUtils.INVALID_TIMESTAMP && + end > parent.mEndTimeMs) { + end = parent.mEndTimeMs; + } + } + TtmlNode node = new TtmlNode(mParser.getName(), attrStr.toString(), null, start, end, + parent, mCurrentRunId); + return node; + } + + private boolean isEndOfDoc() throws XmlPullParserException { + return (mParser.getEventType() == XmlPullParser.END_DOCUMENT); + } + + private static boolean isSupportedTag(String tag) { + if (tag.equals(TtmlUtils.TAG_TT) || tag.equals(TtmlUtils.TAG_HEAD) || + tag.equals(TtmlUtils.TAG_BODY) || tag.equals(TtmlUtils.TAG_DIV) || + tag.equals(TtmlUtils.TAG_P) || tag.equals(TtmlUtils.TAG_SPAN) || + tag.equals(TtmlUtils.TAG_BR) || tag.equals(TtmlUtils.TAG_STYLE) || + tag.equals(TtmlUtils.TAG_STYLING) || tag.equals(TtmlUtils.TAG_LAYOUT) || + tag.equals(TtmlUtils.TAG_REGION) || tag.equals(TtmlUtils.TAG_METADATA) || + tag.equals(TtmlUtils.TAG_SMPTE_IMAGE) || tag.equals(TtmlUtils.TAG_SMPTE_DATA) || + tag.equals(TtmlUtils.TAG_SMPTE_INFORMATION)) { + return true; + } + return false; + } +} + +/** @hide */ +interface TtmlNodeListener { + void onTtmlNodeParsed(TtmlNode node); + void onRootNodeParsed(TtmlNode node); +} + +/** @hide */ +class TtmlTrack extends SubtitleTrack implements TtmlNodeListener { + private static final String TAG = "TtmlTrack"; + + private final TtmlParser mParser = new TtmlParser(this); + private final TtmlRenderingWidget mRenderingWidget; + private String mParsingData; + private Long mCurrentRunID; + + private final LinkedList<TtmlNode> mTtmlNodes; + private final TreeSet<Long> mTimeEvents; + private TtmlNode mRootNode; + + TtmlTrack(TtmlRenderingWidget renderingWidget, MediaFormat format) { + super(format); + + mTtmlNodes = new LinkedList<TtmlNode>(); + mTimeEvents = new TreeSet<Long>(); + mRenderingWidget = renderingWidget; + mParsingData = ""; + } + + @Override + public TtmlRenderingWidget getRenderingWidget() { + return mRenderingWidget; + } + + @Override + public void onData(String data, boolean eos, long runID) { + // implement intermixing restriction for TTML. + synchronized(mParser) { + if (mCurrentRunID != null && runID != mCurrentRunID) { + throw new IllegalStateException( + "Run #" + mCurrentRunID + + " in progress. Cannot process run #" + runID); + } + mCurrentRunID = runID; + mParsingData += data; + if (eos) { + try { + mParser.parse(mParsingData, mCurrentRunID); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + finishedRun(runID); + mParsingData = ""; + mCurrentRunID = null; + } + } + } + + @Override + public void onTtmlNodeParsed(TtmlNode node) { + mTtmlNodes.addLast(node); + addTimeEvents(node); + } + + @Override + public void onRootNodeParsed(TtmlNode node) { + mRootNode = node; + TtmlCue cue = null; + while ((cue = getNextResult()) != null) { + addCue(cue); + } + mRootNode = null; + mTtmlNodes.clear(); + mTimeEvents.clear(); + } + + @Override + public void updateView(Vector<SubtitleTrack.Cue> activeCues) { + if (!mVisible) { + // don't keep the state if we are not visible + return; + } + + if (DEBUG && mTimeProvider != null) { + try { + Log.d(TAG, "at " + + (mTimeProvider.getCurrentTimeUs(false, true) / 1000) + + " ms the active cues are:"); + } catch (IllegalStateException e) { + Log.d(TAG, "at (illegal state) the active cues are:"); + } + } + + mRenderingWidget.setActiveCues(activeCues); + } + + /** + * Returns a {@link TtmlCue} in the presentation time order. + * {@code null} is returned if there is no more timed text to show. + */ + public TtmlCue getNextResult() { + while (mTimeEvents.size() >= 2) { + long start = mTimeEvents.pollFirst(); + long end = mTimeEvents.first(); + List<TtmlNode> activeCues = getActiveNodes(start, end); + if (!activeCues.isEmpty()) { + return new TtmlCue(start, end, + TtmlUtils.applySpacePolicy(TtmlUtils.extractText( + mRootNode, start, end), false), + TtmlUtils.extractTtmlFragment(mRootNode, start, end)); + } + } + return null; + } + + private void addTimeEvents(TtmlNode node) { + mTimeEvents.add(node.mStartTimeMs); + mTimeEvents.add(node.mEndTimeMs); + for (int i = 0; i < node.mChildren.size(); ++i) { + addTimeEvents(node.mChildren.get(i)); + } + } + + private List<TtmlNode> getActiveNodes(long startTimeUs, long endTimeUs) { + List<TtmlNode> activeNodes = new ArrayList<TtmlNode>(); + for (int i = 0; i < mTtmlNodes.size(); ++i) { + TtmlNode node = mTtmlNodes.get(i); + if (node.isActive(startTimeUs, endTimeUs)) { + activeNodes.add(node); + } + } + return activeNodes; + } +} + +/** + * Widget capable of rendering TTML captions. + * + * @hide + */ +class TtmlRenderingWidget extends LinearLayout implements SubtitleTrack.RenderingWidget { + + /** Callback for rendering changes. */ + private OnChangedListener mListener; + private final TextView mTextView; + + public TtmlRenderingWidget(Context context) { + this(context, null); + } + + public TtmlRenderingWidget(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public TtmlRenderingWidget(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public TtmlRenderingWidget(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + // Cannot render text over video when layer type is hardware. + setLayerType(View.LAYER_TYPE_SOFTWARE, null); + + CaptioningManager captionManager = (CaptioningManager) context.getSystemService( + Context.CAPTIONING_SERVICE); + mTextView = new TextView(context); + mTextView.setTextColor(captionManager.getUserStyle().foregroundColor); + addView(mTextView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + mTextView.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL); + } + + @Override + public void setOnChangedListener(OnChangedListener listener) { + mListener = listener; + } + + @Override + public void setSize(int width, int height) { + final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); + final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); + + measure(widthSpec, heightSpec); + layout(0, 0, width, height); + } + + @Override + public void setVisible(boolean visible) { + if (visible) { + setVisibility(View.VISIBLE); + } else { + setVisibility(View.GONE); + } + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + } + + public void setActiveCues(Vector<SubtitleTrack.Cue> activeCues) { + final int count = activeCues.size(); + String subtitleText = ""; + for (int i = 0; i < count; i++) { + TtmlCue cue = (TtmlCue) activeCues.get(i); + subtitleText += cue.mText + "\n"; + } + mTextView.setText(subtitleText); + + if (mListener != null) { + mListener.onChanged(this); + } + } +} diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index 3ff07d9..096550f 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -40,6 +40,7 @@ interface ISession { boolean setRoute(in RouteInfo route); void setRouteOptions(in List<RouteOptions> options); void connectToRoute(in RouteInfo route, in RouteOptions options); + void disconnectFromRoute(in RouteInfo route); void sendRouteCommand(in RouteCommand event, in ResultReceiver cb); // These commands are for the TransportPerformer diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl index f04cbcc..1552513 100644 --- a/media/java/android/media/session/ISessionCallback.aidl +++ b/media/java/android/media/session/ISessionCallback.aidl @@ -31,6 +31,7 @@ oneway interface ISessionCallback { void onMediaButton(in Intent mediaButtonIntent); void onRequestRouteChange(in RouteInfo route); void onRouteConnected(in RouteInfo route, in RouteOptions options); + void onRouteDisconnected(in RouteInfo route, int reason); void onRouteStateChange(int state); void onRouteEvent(in RouteEvent event); diff --git a/media/java/android/media/session/Session.java b/media/java/android/media/session/Session.java index 194679e7..2ffced6 100644 --- a/media/java/android/media/session/Session.java +++ b/media/java/android/media/session/Session.java @@ -86,10 +86,39 @@ public final class Session { */ public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16; + /** + * Indicates the session was disconnected because the user that the session + * belonged to is stopping. + */ + public static final int DISCONNECT_REASON_USER_STOPPING = 1; + + /** + * Indicates the session was disconnected because the provider disconnected + * the route. + */ + public static final int DISCONNECT_REASON_PROVIDER_DISCONNECTED = 2; + + /** + * Indicates the session was disconnected because the route has changed. + */ + public static final int DISCONNECT_REASON_ROUTE_CHANGED = 3; + + /** + * Indicates the session was disconnected because the session owner + * requested it disconnect. + */ + public static final int DISCONNECT_REASON_SESSION_DISCONNECTED = 4; + + /** + * Indicates the session was disconnected because it was destroyed. + */ + public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5; + private static final int MSG_MEDIA_BUTTON = 1; private static final int MSG_COMMAND = 2; private static final int MSG_ROUTE_CHANGE = 3; private static final int MSG_ROUTE_CONNECTED = 4; + private static final int MSG_ROUTE_DISCONNECTED = 5; private static final String KEY_COMMAND = "command"; private static final String KEY_EXTRAS = "extras"; @@ -302,11 +331,15 @@ public final class Session { /** * Disconnect from the current route. After calling you will be switched * back to the default route. - * - * @param route The route to disconnect from. */ - public void disconnect(RouteInfo route) { - // TODO + public void disconnect() { + if (mRoute != null) { + try { + mBinder.disconnectFromRoute(mRoute.getRouteInfo()); + } catch (RemoteException e) { + Log.wtf(TAG, "Error disconnecting from route"); + } + } } /** @@ -406,6 +439,16 @@ public final class Session { } } + private void postRouteDisconnected(RouteInfo route, int reason) { + synchronized (mLock) { + if (mRoute != null && TextUtils.equals(mRoute.getRouteInfo().getId(), route.getId())) { + for (int i = mCallbacks.size() - 1; i >= 0; i--) { + mCallbacks.get(i).post(MSG_ROUTE_DISCONNECTED, mRoute, reason); + } + } + } + } + /** * Receives commands or updates from controllers and routes. An app can * specify what commands and buttons it supports by setting them on the @@ -467,6 +510,11 @@ public final class Session { * <p> * Valid reasons are: * <ul> + * <li>{@link #DISCONNECT_REASON_USER_STOPPING}</li> + * <li>{@link #DISCONNECT_REASON_PROVIDER_DISCONNECTED}</li> + * <li>{@link #DISCONNECT_REASON_ROUTE_CHANGED}</li> + * <li>{@link #DISCONNECT_REASON_SESSION_DISCONNECTED}</li> + * <li>{@link #DISCONNECT_REASON_SESSION_DESTROYED}</li> * </ul> * * @param route The route that disconnected @@ -520,6 +568,14 @@ public final class Session { } @Override + public void onRouteDisconnected(RouteInfo route, int reason) { + Session session = mMediaSession.get(); + if (session != null) { + session.postRouteDisconnected(route, reason); + } + } + + @Override public void onPlay() throws RemoteException { Session session = mMediaSession.get(); if (session != null) { @@ -668,6 +724,9 @@ public final class Session { case MSG_ROUTE_CONNECTED: mCallback.onRouteConnected((Route) msg.obj); break; + case MSG_ROUTE_DISCONNECTED: + mCallback.onRouteDisconnected((Route) msg.obj, msg.arg1); + break; } } } @@ -675,6 +734,10 @@ public final class Session { public void post(int what, Object obj) { obtainMessage(what, obj).sendToTarget(); } + + public void post(int what, Object obj, int arg1) { + obtainMessage(what, arg1, 0, obj).sendToTarget(); + } } private static final class Command { diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java index b28733a..5ab586f 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java @@ -23,19 +23,21 @@ import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.ColorSpaceTransform; import android.hardware.camera2.Face; import android.hardware.camera2.MeteringRectangle; import android.hardware.camera2.Rational; -import android.hardware.camera2.ReprocessFormatsMap; import android.hardware.camera2.RggbChannelVector; import android.hardware.camera2.Size; -import android.hardware.camera2.StreamConfiguration; -import android.hardware.camera2.StreamConfigurationDuration; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.marshal.impl.MarshalQueryableEnum; +import android.hardware.camera2.params.ReprocessFormatsMap; +import android.hardware.camera2.params.StreamConfiguration; +import android.hardware.camera2.params.StreamConfigurationDuration; +import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.TypeReference; import static android.hardware.camera2.impl.CameraMetadataNative.*; @@ -72,6 +74,9 @@ public class CameraMetadataTest extends junit.framework.TestCase { static final int ANDROID_CONTROL_AE_ANTIBANDING_MODE = ANDROID_CONTROL_START; static final int ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION = ANDROID_CONTROL_START + 1; + // From graphics.h + private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22; + @Override public void setUp() { mMetadata = new CameraMetadataNative(); @@ -293,6 +298,28 @@ public class CameraMetadataTest extends junit.framework.TestCase { } } + private static <T, T2> void assertArrayContains(T needle, T2 array) { + if (!array.getClass().isArray()) { + throw new IllegalArgumentException("actual must be array"); + } + + int len = Array.getLength(array); + for (int i = 0; i < len; ++i) { + + Object actualElement = Array.get(array, i); + + if (needle.equals(actualElement)) { + return; + } + } + + fail(String.format( + "could not find element in array (needle %s). " + + "Array was: %s.", + needle, + formatArray(array, len))); + } + private <T> void checkKeyGetAndSet(String keyStr, TypeReference<T> typeToken, T expected, boolean reuse) { Key<T> key = new Key<T>(keyStr, typeToken); @@ -804,18 +831,48 @@ public class CameraMetadataTest extends junit.framework.TestCase { @SmallTest public void testReadWriteReprocessFormatsMap() { - final int RAW_OPAQUE = 0x24; + // final int RAW_OPAQUE = 0x24; // TODO: add RAW_OPAQUE to ImageFormat final int RAW16 = ImageFormat.RAW_SENSOR; final int YUV_420_888 = ImageFormat.YUV_420_888; final int BLOB = 0x21; + // TODO: also test HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED as an output int[] contents = new int[] { - RAW_OPAQUE, 3, RAW16, YUV_420_888, BLOB, + YUV_420_888, 3, YUV_420_888, ImageFormat.NV21, BLOB, RAW16, 2, YUV_420_888, BLOB, + }; // int32 x n - checkKeyMarshal("android.scaler.availableInputOutputFormatsMap", + Key<ReprocessFormatsMap> key = new Key<ReprocessFormatsMap>( + "android.scaler.availableInputOutputFormatsMap", ReprocessFormatsMap.class); + mMetadata.writeValues(key.getTag(), toByteArray(contents)); + + ReprocessFormatsMap map = mMetadata.get(key); + + /* + * Make sure the inputs/outputs were what we expected. + * - Use public image format constants here. + */ + + int[] expectedInputs = new int[] { + YUV_420_888, RAW16 + }; + assertArrayEquals(expectedInputs, map.getInputs()); + + int[] expectedYuvOutputs = new int[] { + YUV_420_888, ImageFormat.NV21, ImageFormat.JPEG, + }; + assertArrayEquals(expectedYuvOutputs, map.getOutputs(ImageFormat.YUV_420_888)); + + int[] expectedRaw16Outputs = new int[] { + YUV_420_888, ImageFormat.JPEG, + }; + assertArrayEquals(expectedRaw16Outputs, map.getOutputs(ImageFormat.RAW_SENSOR)); + + // Finally, do a round-trip check as a sanity + checkKeyMarshal( + "android.scaler.availableInputOutputFormatsMap", new ReprocessFormatsMap(contents), toByteArray(contents) ); @@ -889,68 +946,6 @@ public class CameraMetadataTest extends junit.framework.TestCase { expectedIntValues, availableFormatTag); // - // android.scaler.availableStreamConfigurations (int x n x 4 array) - // - final int OUTPUT = CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT; - int[] availableStreamConfigs = new int[] { - 0x20, 3280, 2464, OUTPUT, // RAW16 - 0x23, 3264, 2448, OUTPUT, // YCbCr_420_888 - 0x23, 3200, 2400, OUTPUT, // YCbCr_420_888 - 0x100, 3264, 2448, OUTPUT, // ImageFormat.JPEG - 0x100, 3200, 2400, OUTPUT, // ImageFormat.JPEG - 0x100, 2592, 1944, OUTPUT, // ImageFormat.JPEG - 0x100, 2048, 1536, OUTPUT, // ImageFormat.JPEG - 0x100, 1920, 1080, OUTPUT // ImageFormat.JPEG - }; - int[] expectedAvailableStreamConfigs = new int[] { - 0x20, 3280, 2464, OUTPUT, // RAW16 - 0x23, 3264, 2448, OUTPUT, // YCbCr_420_888 - 0x23, 3200, 2400, OUTPUT, // YCbCr_420_888 - 0x21, 3264, 2448, OUTPUT, // BLOB - 0x21, 3200, 2400, OUTPUT, // BLOB - 0x21, 2592, 1944, OUTPUT, // BLOB - 0x21, 2048, 1536, OUTPUT, // BLOB - 0x21, 1920, 1080, OUTPUT // BLOB - }; - int availableStreamConfigTag = - CameraMetadataNative.getTag("android.scaler.availableStreamConfigurations"); - - Key<int[]> configKey = CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS; - validateArrayMetadataReadWriteOverride(configKey, availableStreamConfigs, - expectedAvailableStreamConfigs, availableStreamConfigTag); - - // - // android.scaler.availableMinFrameDurations (int x n x 4 array) - - // - long[] availableMinDurations = new long[] { - 0x20, 3280, 2464, 33333336, // RAW16 - 0x23, 3264, 2448, 33333336, // YCbCr_420_888 - 0x23, 3200, 2400, 33333336, // YCbCr_420_888 - 0x100, 3264, 2448, 33333336, // ImageFormat.JPEG - 0x100, 3200, 2400, 33333336, // ImageFormat.JPEG - 0x100, 2592, 1944, 33333336, // ImageFormat.JPEG - 0x100, 2048, 1536, 33333336, // ImageFormat.JPEG - 0x100, 1920, 1080, 33333336 // ImageFormat.JPEG - }; - long[] expectedAvailableMinDurations = new long[] { - 0x20, 3280, 2464, 33333336, // RAW16 - 0x23, 3264, 2448, 33333336, // YCbCr_420_888 - 0x23, 3200, 2400, 33333336, // YCbCr_420_888 - 0x21, 3264, 2448, 33333336, // BLOB - 0x21, 3200, 2400, 33333336, // BLOB - 0x21, 2592, 1944, 33333336, // BLOB - 0x21, 2048, 1536, 33333336, // BLOB - 0x21, 1920, 1080, 33333336 // BLOB - }; - int availableMinDurationsTag = - CameraMetadataNative.getTag("android.scaler.availableMinFrameDurations"); - - Key<long[]> durationKey = CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS; - validateArrayMetadataReadWriteOverride(durationKey, availableMinDurations, - expectedAvailableMinDurations, availableMinDurationsTag); - - // // android.statistics.faces (Face x n array) // int[] expectedFaceIds = new int[] {1, 2, 3, 4, 5}; @@ -1015,14 +1010,238 @@ public class CameraMetadataTest extends junit.framework.TestCase { } /** + * Set the raw native value of the available stream configurations; ensure that + * the read-out managed value is consistent with what we write in. + */ + @SmallTest + public void testOverrideStreamConfigurationMap() { + + /* + * First, write all the raw values: + * - availableStreamConfigurations + * - availableMinFrameDurations + * - availableStallDurations + * + * Then, read this out as a synthetic multi-key 'streamConfigurationMap' + * + * Finally, validate that the map was unmarshaled correctly + * and is converting the internal formats to public formats properly. + */ + + // + // android.scaler.availableStreamConfigurations (int x n x 4 array) + // + final int OUTPUT = 0; + final int INPUT = 1; + int[] rawAvailableStreamConfigs = new int[] { + 0x20, 3280, 2464, OUTPUT, // RAW16 + 0x23, 3264, 2448, OUTPUT, // YCbCr_420_888 + 0x23, 3200, 2400, OUTPUT, // YCbCr_420_888 + 0x21, 3264, 2448, OUTPUT, // BLOB + 0x21, 3200, 2400, OUTPUT, // BLOB + 0x21, 2592, 1944, OUTPUT, // BLOB + 0x21, 2048, 1536, OUTPUT, // BLOB + 0x21, 1920, 1080, OUTPUT, // BLOB + 0x22, 640, 480, OUTPUT, // IMPLEMENTATION_DEFINED + 0x20, 320, 240, INPUT, // RAW16 + }; + Key<StreamConfiguration[]> configKey = + CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS; + mMetadata.writeValues(configKey.getTag(), + toByteArray(rawAvailableStreamConfigs)); + + // + // android.scaler.availableMinFrameDurations (int x n x 4 array) + // + long[] expectedAvailableMinDurations = new long[] { + 0x20, 3280, 2464, 33333331, // RAW16 + 0x23, 3264, 2448, 33333332, // YCbCr_420_888 + 0x23, 3200, 2400, 33333333, // YCbCr_420_888 + 0x100, 3264, 2448, 33333334, // ImageFormat.JPEG + 0x100, 3200, 2400, 33333335, // ImageFormat.JPEG + 0x100, 2592, 1944, 33333336, // ImageFormat.JPEG + 0x100, 2048, 1536, 33333337, // ImageFormat.JPEG + 0x100, 1920, 1080, 33333338 // ImageFormat.JPEG + }; + long[] rawAvailableMinDurations = new long[] { + 0x20, 3280, 2464, 33333331, // RAW16 + 0x23, 3264, 2448, 33333332, // YCbCr_420_888 + 0x23, 3200, 2400, 33333333, // YCbCr_420_888 + 0x21, 3264, 2448, 33333334, // BLOB + 0x21, 3200, 2400, 33333335, // BLOB + 0x21, 2592, 1944, 33333336, // BLOB + 0x21, 2048, 1536, 33333337, // BLOB + 0x21, 1920, 1080, 33333338 // BLOB + }; + Key<StreamConfigurationDuration[]> durationKey = + CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS; + mMetadata.writeValues(durationKey.getTag(), + toByteArray(rawAvailableMinDurations)); + + // + // android.scaler.availableStallDurations (int x n x 4 array) + // + long[] expectedAvailableStallDurations = new long[] { + 0x20, 3280, 2464, 0, // RAW16 + 0x23, 3264, 2448, 0, // YCbCr_420_888 + 0x23, 3200, 2400, 0, // YCbCr_420_888 + 0x100, 3264, 2448, 33333334, // ImageFormat.JPEG + 0x100, 3200, 2400, 33333335, // ImageFormat.JPEG + 0x100, 2592, 1944, 33333336, // ImageFormat.JPEG + 0x100, 2048, 1536, 33333337, // ImageFormat.JPEG + 0x100, 1920, 1080, 33333338 // ImageFormat.JPEG + }; + // Note: RAW16 and YUV_420_888 omitted intentionally; omitted values should default to 0 + long[] rawAvailableStallDurations = new long[] { + 0x21, 3264, 2448, 33333334, // BLOB + 0x21, 3200, 2400, 33333335, // BLOB + 0x21, 2592, 1944, 33333336, // BLOB + 0x21, 2048, 1536, 33333337, // BLOB + 0x21, 1920, 1080, 33333338 // BLOB + }; + Key<StreamConfigurationDuration[]> stallDurationKey = + CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS; + mMetadata.writeValues(stallDurationKey.getTag(), + toByteArray(rawAvailableStallDurations)); + + // + // android.scaler.streamConfigurationMap (synthetic as StreamConfigurationMap) + // + StreamConfigurationMap streamConfigMap = mMetadata.get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + + // Inputs + checkStreamConfigurationMapByFormatSize( + streamConfigMap, ImageFormat.RAW_SENSOR, 320, 240, /*output*/false); + + // Outputs + checkStreamConfigurationMapByFormatSize( + streamConfigMap, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, 640, 480, /*output*/true); + checkStreamConfigurationMapByFormatSize( + streamConfigMap, ImageFormat.JPEG, 1920, 1080, /*output*/true); + checkStreamConfigurationMapByFormatSize( + streamConfigMap, ImageFormat.JPEG, 2048, 1536, /*output*/true); + checkStreamConfigurationMapByFormatSize( + streamConfigMap, ImageFormat.JPEG, 2592, 1944, /*output*/true); + checkStreamConfigurationMapByFormatSize( + streamConfigMap, ImageFormat.JPEG, 3200, 2400, /*output*/true); + checkStreamConfigurationMapByFormatSize( + streamConfigMap, ImageFormat.YUV_420_888, 3200, 2400, /*output*/true); + checkStreamConfigurationMapByFormatSize( + streamConfigMap, ImageFormat.YUV_420_888, 3264, 2448, /*output*/true); + checkStreamConfigurationMapByFormatSize( + streamConfigMap, ImageFormat.RAW_SENSOR, 3280, 2464, /*output*/true); + + // Min Frame Durations + + final int DURATION_TUPLE_SIZE = 4; + for (int i = 0; i < expectedAvailableMinDurations.length; i += DURATION_TUPLE_SIZE) { + checkStreamConfigurationMapDurationByFormatSize( + streamConfigMap, + (int)expectedAvailableMinDurations[i], + (int)expectedAvailableMinDurations[i+1], + (int)expectedAvailableMinDurations[i+2], + Duration.MinFrame, + expectedAvailableMinDurations[i+3]); + } + + // Stall Frame Durations + + for (int i = 0; i < expectedAvailableStallDurations.length; i += DURATION_TUPLE_SIZE) { + checkStreamConfigurationMapDurationByFormatSize( + streamConfigMap, + (int)expectedAvailableStallDurations[i], + (int)expectedAvailableStallDurations[i+1], + (int)expectedAvailableStallDurations[i+2], + Duration.Stall, + expectedAvailableStallDurations[i+3]); + } + } + + private static void checkStreamConfigurationMapByFormatSize(StreamConfigurationMap configMap, + int format, int width, int height, + boolean output) { + + /** arbitrary class for which StreamConfigurationMap#isOutputSupportedFor(Class) is true */ + final Class<?> IMPLEMENTATION_DEFINED_OUTPUT_CLASS = SurfaceTexture.class; + + android.util.Size[] sizes; + int[] formats; + + if (output) { + if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { + sizes = configMap.getOutputSizes(IMPLEMENTATION_DEFINED_OUTPUT_CLASS); + // in this case the 'is output format supported' is vacuously true + formats = new int[] { HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED }; + } else { + sizes = configMap.getOutputSizes(format); + formats = configMap.getOutputFormats(); + assertTrue("Format must be supported by stream configuration map", + configMap.isOutputSupportedFor(format)); + } + } else { + // NOTE: No function to do input sizes from IMPL_DEFINED, so it would just fail for that + sizes = configMap.getInputSizes(format); + formats = configMap.getInputFormats(); + } + + android.util.Size expectedSize = new android.util.Size(width, height); + + assertArrayContains(format, formats); + assertArrayContains(expectedSize, sizes); + } + + private enum Duration { + MinFrame, + Stall + } + + private static void checkStreamConfigurationMapDurationByFormatSize( + StreamConfigurationMap configMap, + int format, int width, int height, Duration durationKind, long expectedDuration) { + + /** arbitrary class for which StreamConfigurationMap#isOutputSupportedFor(Class) is true */ + final Class<?> IMPLEMENTATION_DEFINED_OUTPUT_CLASS = SurfaceTexture.class; + + long actualDuration; + + android.util.Size size = new android.util.Size(width, height); + switch (durationKind) { + case MinFrame: + if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { + actualDuration = configMap.getOutputMinFrameDuration( + IMPLEMENTATION_DEFINED_OUTPUT_CLASS, size); + } else { + actualDuration = configMap.getOutputMinFrameDuration(format, size); + } + + break; + case Stall: + if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { + actualDuration = configMap.getOutputStallDuration( + IMPLEMENTATION_DEFINED_OUTPUT_CLASS, size); + } else { + actualDuration = configMap.getOutputStallDuration(format, size); + } + + break; + default: + throw new AssertionError(); + } + + assertEquals("Expected " + durationKind + " to match actual value", expectedDuration, + actualDuration); + } + + /** * Validate metadata array tag read/write override. * * <p>Only support long and int array for now, can be easily extend to support other * primitive arrays.</p> */ - private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T writeValues, - T readValues, int tag) { - Class<?> type = writeValues.getClass(); + private <T> void validateArrayMetadataReadWriteOverride(Key<T> key, T expectedWriteValues, + T expectedReadValues, int tag) { + Class<?> type = expectedWriteValues.getClass(); if (!type.isArray()) { throw new IllegalArgumentException("This function expects an key with array type"); } else if (type != int[].class && type != long[].class) { @@ -1030,13 +1249,13 @@ public class CameraMetadataTest extends junit.framework.TestCase { } // Write - mMetadata.set(key, writeValues); + mMetadata.set(key, expectedWriteValues); byte[] readOutValues = mMetadata.readValues(tag); ByteBuffer bf = ByteBuffer.wrap(readOutValues).order(ByteOrder.nativeOrder()); - int readValuesLength = Array.getLength(readValues); + int readValuesLength = Array.getLength(expectedReadValues); int readValuesNumBytes = readValuesLength * 4; if (type == long[].class) { readValuesNumBytes = readValuesLength * 8; @@ -1045,9 +1264,9 @@ public class CameraMetadataTest extends junit.framework.TestCase { assertEquals(readValuesNumBytes, readOutValues.length); for (int i = 0; i < readValuesLength; ++i) { if (type == int[].class) { - assertEquals(Array.getInt(readValues, i), bf.getInt()); + assertEquals(Array.getInt(expectedReadValues, i), bf.getInt()); } else if (type == long[].class) { - assertEquals(Array.getLong(readValues, i), bf.getLong()); + assertEquals(Array.getLong(expectedReadValues, i), bf.getLong()); } } @@ -1057,16 +1276,16 @@ public class CameraMetadataTest extends junit.framework.TestCase { ByteBuffer.wrap(readOutValuesAsByteArray).order(ByteOrder.nativeOrder()); for (int i = 0; i < readValuesLength; ++i) { if (type == int[].class) { - readOutValuesByteBuffer.putInt(Array.getInt(readValues, i)); + readOutValuesByteBuffer.putInt(Array.getInt(expectedReadValues, i)); } else if (type == long[].class) { - readOutValuesByteBuffer.putLong(Array.getLong(readValues, i)); + readOutValuesByteBuffer.putLong(Array.getLong(expectedReadValues, i)); } } mMetadata.writeValues(tag, readOutValuesAsByteArray); T result = mMetadata.get(key); assertNotNull(key.getName() + " result shouldn't be null", result); - assertArrayEquals(writeValues, result); + assertArrayEquals(expectedWriteValues, result); } // TODO: move somewhere else diff --git a/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml b/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml index b48e011..b363ab4 100644 --- a/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml +++ b/packages/Keyguard/test/SampleTrustAgent/res/xml/sample_trust_agent.xml @@ -14,5 +14,5 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<trust_agent xmlns:android="http://schemas.android.com/apk/res/android" +<trust-agent xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity=".SampleTrustAgentSettings" /> diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java index 25406d6..a51ea75 100644 --- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java +++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java @@ -31,7 +31,7 @@ public class SampleTrustAgent extends TrustAgentService { LocalBroadcastManager mLocalBroadcastManager; - private static final String ACTION_ENABLE_TRUST = "action.sample_trust_agent.enable_trust"; + private static final String ACTION_GRANT_TRUST = "action.sample_trust_agent.grant_trust"; private static final String ACTION_REVOKE_TRUST = "action.sample_trust_agent.revoke_trust"; private static final String EXTRA_MESSAGE = "extra.message"; @@ -45,14 +45,14 @@ public class SampleTrustAgent extends TrustAgentService { public void onCreate() { super.onCreate(); IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_ENABLE_TRUST); + filter.addAction(ACTION_GRANT_TRUST); filter.addAction(ACTION_REVOKE_TRUST); mLocalBroadcastManager = LocalBroadcastManager.getInstance(this); mLocalBroadcastManager.registerReceiver(mReceiver, filter); } @Override - protected void onUnlockAttempt(boolean successful) { + public void onUnlockAttempt(boolean successful) { if (getReportUnlockAttempts(this)) { Toast.makeText(this, "onUnlockAttempt(successful=" + successful + ")", Toast.LENGTH_SHORT).show(); @@ -69,8 +69,8 @@ public class SampleTrustAgent extends TrustAgentService { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (ACTION_ENABLE_TRUST.equals(action)) { - enableTrust(intent.getStringExtra(EXTRA_MESSAGE), + if (ACTION_GRANT_TRUST.equals(action)) { + grantTrust(intent.getStringExtra(EXTRA_MESSAGE), intent.getLongExtra(EXTRA_DURATION, 0), false /* initiatedByUser */); } else if (ACTION_REVOKE_TRUST.equals(action)) { @@ -79,9 +79,9 @@ public class SampleTrustAgent extends TrustAgentService { } }; - public static void sendEnableTrust(Context context, + public static void sendGrantTrust(Context context, String message, long durationMs, Bundle extra) { - Intent intent = new Intent(ACTION_ENABLE_TRUST); + Intent intent = new Intent(ACTION_GRANT_TRUST); intent.putExtra(EXTRA_MESSAGE, message); intent.putExtra(EXTRA_DURATION, durationMs); intent.putExtra(EXTRA_EXTRA, extra); diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java index 0a6f675..8e293fb 100644 --- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java +++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgentSettings.java @@ -19,7 +19,6 @@ package com.android.trustagent.test; import android.annotation.Nullable; import android.app.Activity; import android.os.Bundle; -import android.preference.CheckBoxPreference; import android.view.View; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -53,7 +52,7 @@ public class SampleTrustAgentSettings extends Activity implements View.OnClickLi public void onClick(View v) { int id = v.getId(); if (id == R.id.enable_trust) { - SampleTrustAgent.sendEnableTrust(this, "SampleTrustAgent", TRUST_DURATION_MS, + SampleTrustAgent.sendGrantTrust(this, "SampleTrustAgent", TRUST_DURATION_MS, null /* extra */); } else if (id == R.id.revoke_trust) { SampleTrustAgent.sendRevokeTrust(this); diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_location_off.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_location_off.png Binary files differdeleted file mode 100644 index 189f27b..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_qs_location_off.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_location_on.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_location_on.png Binary files differdeleted file mode 100644 index b03d30c..0000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_qs_location_on.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_location_off.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_location_off.png Binary files differdeleted file mode 100644 index b692107..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_qs_location_off.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_location_on.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_location_on.png Binary files differdeleted file mode 100644 index 867c57d..0000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_qs_location_on.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_location_off.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_location_off.png Binary files differdeleted file mode 100644 index 7ce8f83..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_location_off.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_location_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_location_on.png Binary files differdeleted file mode 100644 index 6300bdc..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_location_on.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location_off.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location_off.png Binary files differdeleted file mode 100644 index c14c1bb..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location_off.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location_on.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location_on.png Binary files differdeleted file mode 100644 index d6d4c70..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_location_on.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/ic_location_24_01.xml b/packages/SystemUI/res/drawable/ic_location_24_01.xml new file mode 100644 index 0000000..ff37d9a --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_01.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="24.0" + android:viewportHeight="24.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"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_02.xml b/packages/SystemUI/res/drawable/ic_location_24_02.xml new file mode 100644 index 0000000..bb4465f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_02.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="24.0" + android:viewportHeight="24.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"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_03.xml b/packages/SystemUI/res/drawable/ic_location_24_03.xml new file mode 100644 index 0000000..956a8c3 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_03.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="24.0" + android:viewportHeight="24.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"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_04.xml b/packages/SystemUI/res/drawable/ic_location_24_04.xml new file mode 100644 index 0000000..0c0fb3b --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_04.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="24.0" + android:viewportHeight="24.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"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_05.xml b/packages/SystemUI/res/drawable/ic_location_24_05.xml new file mode 100644 index 0000000..1a21e2f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_05.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="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_location_24_06.xml b/packages/SystemUI/res/drawable/ic_location_24_06.xml new file mode 100644 index 0000000..25c9ae5 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_06.xml @@ -0,0 +1,33 @@ +<!-- +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:strokeWidth=".35" + android:fill="#00000000" + android:stroke="#CCCCCC"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_07.xml b/packages/SystemUI/res/drawable/ic_location_24_07.xml new file mode 100644 index 0000000..a69c3a2 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_07.xml @@ -0,0 +1,30 @@ +<!-- +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:strokeWidth="0.65" + android:fill="#00000000" + android:stroke="#CCCCCC"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_08.xml b/packages/SystemUI/res/drawable/ic_location_24_08.xml new file mode 100644 index 0000000..c89c047 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_08.xml @@ -0,0 +1,30 @@ +<!-- +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:strokeWidth="0.8" + android:fill="#00000000" + android:stroke="#CCCCCC"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_09.xml b/packages/SystemUI/res/drawable/ic_location_24_09.xml new file mode 100644 index 0000000..96bb6ce --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_09.xml @@ -0,0 +1,30 @@ +<!-- +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:strokeWidth="0.9" + android:fill="#00000000" + android:stroke="#CCCCCC"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_10.xml b/packages/SystemUI/res/drawable/ic_location_24_10.xml new file mode 100644 index 0000000..aced4bd --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_10.xml @@ -0,0 +1,30 @@ +<!-- +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:strokeWidth="0.95" + android:fill="#00000000" + android:stroke="#CCCCCC"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_24_11.xml b/packages/SystemUI/res/drawable/ic_location_24_11.xml new file mode 100644 index 0000000..578308e --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_24_11.xml @@ -0,0 +1,30 @@ +<!-- +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,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="#00000000" + android:stroke="#CCCCCC" + android:strokeWidth="1.0"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_location_off_anim.xml b/packages/SystemUI/res/drawable/ic_location_off_anim.xml new file mode 100644 index 0000000..864eda1 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_off_anim.xml @@ -0,0 +1,31 @@ +<?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. +--> +<animation-list + xmlns:android="http://schemas.android.com/apk/res/android" + android:oneshot="true"> + <item android:drawable="@drawable/ic_location_24_01" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_02" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_03" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_04" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_05" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_06" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_07" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_08" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_09" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_10" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_11" android:duration="16" /> +</animation-list> diff --git a/packages/SystemUI/res/drawable/ic_location_on_anim.xml b/packages/SystemUI/res/drawable/ic_location_on_anim.xml new file mode 100644 index 0000000..65a8afe --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_location_on_anim.xml @@ -0,0 +1,31 @@ +<?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. +--> +<animation-list + xmlns:android="http://schemas.android.com/apk/res/android" + android:oneshot="true"> + <item android:drawable="@drawable/ic_location_24_11" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_10" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_09" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_08" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_07" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_06" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_05" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_04" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_03" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_02" android:duration="16" /> + <item android:drawable="@drawable/ic_location_24_01" android:duration="16" /> +</animation-list> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 7de1bd0..9582b21 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -22,6 +22,7 @@ <drawable name="system_bar_background">@color/system_bar_background_opaque</drawable> <color name="system_bar_background_opaque">#ff000000</color> <color name="system_bar_background_semi_transparent">#66000000</color> <!-- 40% black --> + <color name="system_bar_background_transparent">#00000000</color> <color name="notification_panel_solid_background">#ff000000</color> <drawable name="status_bar_recents_app_thumbnail_background">#88000000</drawable> <color name="status_bar_recents_app_label_color">#ffffffff</color> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 1273e74..9536b12 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -22,8 +22,9 @@ <!-- Alternate Recents theme --> <style name="RecentsTheme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar"> - <item name="android:windowTranslucentStatus">true</item> - <item name="android:windowTranslucentNavigation">true</item> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:navigationBarColor">@android:color/transparent</item> + <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item> </style> 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 d32f98f..176e05c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -16,6 +16,8 @@ package com.android.systemui.qs.tiles; +import android.graphics.drawable.AnimationDrawable; + import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.policy.LocationController; @@ -56,16 +58,28 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { protected void handleUpdateState(BooleanState state, Object arg) { final boolean locationEnabled = mController.isLocationEnabled(); state.visible = true; - state.value = locationEnabled; - state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_location); + if (state.value != locationEnabled) { + state.value = locationEnabled; + final AnimationDrawable d = (AnimationDrawable) mContext.getDrawable(locationEnabled + ? R.drawable.ic_location_on_anim + : R.drawable.ic_location_off_anim); + state.icon = d; + mUiHandler.post(new Runnable() { + @Override + public void run() { + d.start(); + } + }); + } + //state.icon = mHost.getVectorDrawable(R.drawable.ic_qs_location); if (locationEnabled) { - state.iconId = R.drawable.ic_qs_location_on; + if (state.icon == null) state.iconId = R.drawable.ic_location_24_01; 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 { - state.iconId = R.drawable.ic_qs_location_off; + if (state.icon == null) state.iconId = R.drawable.ic_location_24_11; state.label = mContext.getString(R.string.quick_settings_location_off_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_location, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 91df9ef..5e8c769 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.MotionEvent; @@ -45,6 +46,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private int mBgResId = R.drawable.notification_quantum_bg; private int mDimmedBgResId = R.drawable.notification_quantum_bg_dim; + private int mBgTint = 0; + private int mDimmedBgTint = 0; + /** * Flag to indicate that the notification has been touched once and the second touch will * click it. @@ -209,17 +213,23 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView * @param bgResId The background resource to use in normal state. * @param dimmedBgResId The background resource to use in dimmed state. */ - public void setBackgroundResourceIds(int bgResId, int dimmedBgResId) { + public void setBackgroundResourceIds(int bgResId, int bgTint, int dimmedBgResId, int dimmedTint) { mBgResId = bgResId; + mBgTint = bgTint; mDimmedBgResId = dimmedBgResId; + mDimmedBgTint = dimmedTint; updateBackgroundResource(); } + public void setBackgroundResourceIds(int bgResId, int dimmedBgResId) { + setBackgroundResourceIds(bgResId, 0, dimmedBgResId, 0); + } + private void fadeBackgroundResource() { if (mDimmed) { - setBackgroundDimmed(mDimmedBgResId); + setBackgroundDimmed(mDimmedBgResId, mDimmedBgTint); } else { - setBackgroundNormal(mBgResId); + setBackgroundNormal(mBgResId, mBgTint); } int startAlpha = mDimmed ? 255 : 0; int endAlpha = mDimmed ? 0 : 255; @@ -256,12 +266,12 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private void updateBackgroundResource() { if (mDimmed) { - setBackgroundDimmed(mDimmedBgResId); + setBackgroundDimmed(mDimmedBgResId, mDimmedBgTint); mBackgroundDimmed.setAlpha(255); setBackgroundNormal(null); } else { setBackgroundDimmed(null); - setBackgroundNormal(mBgResId); + setBackgroundNormal(mBgResId, mBgTint); mBackgroundNormal.setAlpha(255); } } @@ -295,12 +305,20 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView invalidate(); } - private void setBackgroundNormal(int drawableResId) { - setBackgroundNormal(getResources().getDrawable(drawableResId)); + private void setBackgroundNormal(int drawableResId, int tintColor) { + final Drawable d = getResources().getDrawable(drawableResId); + if (tintColor != 0) { + d.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP); + } + setBackgroundNormal(d); } - private void setBackgroundDimmed(int drawableResId) { - setBackgroundDimmed(getResources().getDrawable(drawableResId)); + private void setBackgroundDimmed(int drawableResId, int tintColor) { + final Drawable d = getResources().getDrawable(drawableResId); + if (tintColor != 0) { + d.setColorFilter(tintColor, PorterDuff.Mode.SRC_ATOP); + } + setBackgroundDimmed(d); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 898f06e..6090948 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -428,14 +428,16 @@ public abstract class BaseStatusBar extends SystemUI implements protected void applyLegacyRowBackground(StatusBarNotification sbn, NotificationData.Entry entry) { + int version = 0; + try { + ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0); + version = info.targetSdkVersion; + } catch (NameNotFoundException ex) { + Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); + } + if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) { - int version = 0; - try { - ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.getPackageName(), 0); - version = info.targetSdkVersion; - } catch (NameNotFoundException ex) { - Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex); - } + // 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) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java index eb63a54..a41ec22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java @@ -43,6 +43,7 @@ public class BarTransitions { public static final int MODE_SEMI_TRANSPARENT = 1; public static final int MODE_TRANSLUCENT = 2; public static final int MODE_LIGHTS_OUT = 3; + public static final int MODE_TRANSPARENT = 4; public static final int LIGHTS_IN_DURATION = 250; public static final int LIGHTS_OUT_DURATION = 750; @@ -69,7 +70,8 @@ public class BarTransitions { public void transitionTo(int mode, boolean animate) { // low-end devices do not support translucent modes, fallback to opaque - if (!HIGH_END && (mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT)) { + if (!HIGH_END && (mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT + || mode == MODE_TRANSPARENT)) { mode = MODE_OPAQUE; } if (mMode == mode) return; @@ -97,6 +99,7 @@ public class BarTransitions { if (mode == MODE_SEMI_TRANSPARENT) return "MODE_SEMI_TRANSPARENT"; if (mode == MODE_TRANSLUCENT) return "MODE_TRANSLUCENT"; if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT"; + if (mode == MODE_TRANSPARENT) return "MODE_TRANSPARENT"; throw new IllegalArgumentException("Unknown mode " + mode); } @@ -111,6 +114,7 @@ public class BarTransitions { private static class BarBackgroundDrawable extends Drawable { private final int mOpaque; private final int mSemiTransparent; + private final int mTransparent; private final Drawable mGradient; private final TimeInterpolator mInterpolator; @@ -130,9 +134,11 @@ public class BarTransitions { if (DEBUG_COLORS) { mOpaque = 0xff0000ff; mSemiTransparent = 0x7f0000ff; + mTransparent = 0x2f0000ff; } else { mOpaque = res.getColor(R.color.system_bar_background_opaque); mSemiTransparent = res.getColor(R.color.system_bar_background_semi_transparent); + mTransparent = res.getColor(R.color.system_bar_background_transparent); } mGradient = res.getDrawable(gradientResourceId); mInterpolator = new LinearInterpolator(); @@ -184,9 +190,11 @@ public class BarTransitions { public void draw(Canvas canvas) { int targetGradientAlpha = 0, targetColor = 0; if (mMode == MODE_TRANSLUCENT) { - targetGradientAlpha = 0xff; + targetColor = mSemiTransparent; } else if (mMode == MODE_SEMI_TRANSPARENT) { targetColor = mSemiTransparent; + } else if (mMode == MODE_TRANSPARENT) { + targetColor = mTransparent; } else { targetColor = mOpaque; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index a0582ee..c83b479 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -61,7 +61,7 @@ public final class NavigationBarTransitions extends BarTransitions { @Override public void transitionTo(int mode, boolean animate) { mRequestedMode = mode; - if (mVertical && mode == MODE_TRANSLUCENT) { + if (mVertical && (mode == MODE_TRANSLUCENT || mode == MODE_TRANSPARENT)) { // translucent mode not allowed when vertical mode = MODE_OPAQUE; } 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 9054fe3..b9f5ab2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -78,6 +78,7 @@ public class NotificationPanelView extends PanelView implements private boolean mQsExpansionEnabled = true; private ValueAnimator mQsExpansionAnimator; private FlingAnimationUtils mFlingAnimationUtils; + private int mStatusBarMinHeight; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -116,6 +117,8 @@ public class NotificationPanelView extends PanelView implements R.dimen.notifications_top_padding); mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height); mFlingAnimationUtils = new FlingAnimationUtils(getContext()); + mStatusBarMinHeight = getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); } @Override @@ -520,7 +523,9 @@ public class NotificationPanelView extends PanelView implements int notificationMarginBottom = mStackScrollerContainer.getPaddingBottom(); int emptyBottomMargin = notificationMarginBottom + mNotificationStackScroller.getEmptyBottomMargin(); - return maxPanelHeight - emptyBottomMargin; + int maxHeight = maxPanelHeight - emptyBottomMargin; + maxHeight = Math.max(maxHeight, mStatusBarMinHeight); + return maxHeight; } return super.getMaxPanelHeight(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java index 324d6f3..8800625 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java @@ -224,6 +224,5 @@ public class PanelBar extends FrameLayout { public void onTrackingStopped(PanelView panel) { mTracking = false; - panelExpansionChanged(panel, panel.getExpandedFraction()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java index c229a09..d7f34d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelHolder.java @@ -77,12 +77,6 @@ public class PanelHolder extends FrameLayout { event.getActionMasked(), (int) event.getX(), (int) event.getY()); } } - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - PanelBar.LOG("PanelHolder got touch in open air, closing panels"); - mBar.collapseAllPanels(true); - break; - } return false; } 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 8c70517..dde2ebb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -20,6 +20,7 @@ import android.animation.ObjectAnimator; import android.animation.TimeAnimator; import android.animation.TimeAnimator.TimeListener; import android.content.Context; +import android.content.res.Configuration; import android.content.res.Resources; import android.util.AttributeSet; import android.util.Log; @@ -218,7 +219,7 @@ public class PanelView extends FrameLayout { }; private float mVel, mAccel; - protected int mMaxPanelHeight = 0; + protected int mMaxPanelHeight = -1; private String mViewName; private float mInitialTouchY; private float mInitialTouchX; @@ -617,7 +618,8 @@ public class PanelView extends FrameLayout { // Did one of our children change size? int newHeight = getMeasuredHeight(); - if (newHeight != mMaxPanelHeight) { + if (newHeight > mMaxPanelHeight) { + // we only adapt the max height if it's bigger mMaxPanelHeight = newHeight; // If the user isn't actively poking us, let's rubberband to the content if (!mTracking && !mTimeAnimator.isStarted() @@ -695,6 +697,12 @@ public class PanelView extends FrameLayout { mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mMaxPanelHeight = -1; + } + protected void onHeightUpdated(float expandedHeight) { requestLayout(); } @@ -706,6 +714,7 @@ public class PanelView extends FrameLayout { * @return the default implementation simply returns the maximum height. */ protected int getMaxPanelHeight() { + mMaxPanelHeight = Math.max(mMaxPanelHeight, getHeight()); return mMaxPanelHeight; } 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 23b0594..d3b5f96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -26,6 +26,7 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OU import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; +import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -1870,6 +1871,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int barMode(int vis, int transientFlag, int translucentFlag) { return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT + : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT : MODE_OPAQUE; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java index 8406565..8520f40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java @@ -68,7 +68,8 @@ public final class PhoneStatusBarTransitions extends BarTransitions { } private boolean isOpaque(int mode) { - return !(mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT); + return !(mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT + || mode == MODE_TRANSPARENT); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index 46a637b..b7bf6cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -75,8 +75,7 @@ public class StatusBarWindowManager { | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION - | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, + | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, PixelFormat.TRANSLUCENT); mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; mLp.gravity = Gravity.TOP; diff --git a/policy/src/com/android/internal/policy/impl/BarController.java b/policy/src/com/android/internal/policy/impl/BarController.java index fc49a569..49e1072 100644 --- a/policy/src/com/android/internal/policy/impl/BarController.java +++ b/policy/src/com/android/internal/policy/impl/BarController.java @@ -108,13 +108,20 @@ public class BarController { if (mWin != null) { if (win != null && (win.getAttrs().privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) { - if ((PolicyControl.getWindowFlags(win, null) & mTranslucentWmFlag) != 0) { + int fl = PolicyControl.getWindowFlags(win, null); + if ((fl & mTranslucentWmFlag) != 0) { vis |= mTranslucentFlag; } else { vis &= ~mTranslucentFlag; } + if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { + vis |= View.SYSTEM_UI_TRANSPARENT; + } else { + vis &= ~View.SYSTEM_UI_TRANSPARENT; + } } else { vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag); + vis = (vis & View.SYSTEM_UI_TRANSPARENT) | (oldVis & View.SYSTEM_UI_TRANSPARENT); } } return vis; @@ -230,7 +237,8 @@ public class BarController { vis |= mTransientFlag; // ignore clear requests until transition completes vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile } - if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0) { + if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 || + ((vis | oldVis) & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) { mLastTranslucent = SystemClock.uptimeMillis(); } return vis; diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index fec9dda..673ce0b 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -183,11 +183,17 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac mDialog = createDialog(); prepareDialog(); - WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes(); - attrs.setTitle("GlobalActions"); - mDialog.getWindow().setAttributes(attrs); - mDialog.show(); - mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND); + // If we only have 1 item and it's a simple press action, just do this action. + if (mAdapter.getCount() == 1 + && mAdapter.getItem(0) instanceof SinglePressAction) { + ((SinglePressAction) mAdapter.getItem(0)).onPress(); + } else { + WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes(); + attrs.setTitle("GlobalActions"); + mDialog.getWindow().setAttributes(attrs); + mDialog.show(); + mDialog.getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_DISABLE_EXPAND); + } } /** @@ -398,7 +404,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac @Override public boolean showBeforeProvisioning() { - return false; + return true; } }; } @@ -1024,6 +1030,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac mEnableAccessibilityController = null; super.setCanceledOnTouchOutside(true); } + super.onStart(); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 6341a0c..1b96f1f 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -38,9 +38,7 @@ import com.android.internal.widget.ActionBarOverlayLayout; import com.android.internal.widget.ActionBarView; import com.android.internal.widget.SwipeDismissLayout; -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.app.ActivityOptions; +import android.app.ActivityManager; import android.app.KeyguardManager; import android.content.Context; import android.content.pm.ActivityInfo; @@ -49,6 +47,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -56,22 +55,15 @@ import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; -import android.transition.ChangeBounds; -import android.transition.Explode; -import android.transition.Fade; -import android.transition.MoveImage; import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionInflater; import android.transition.TransitionManager; -import android.transition.TransitionSet; import android.util.AndroidRuntimeException; -import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -98,7 +90,6 @@ import android.view.ViewManager; import android.view.ViewParent; import android.view.ViewRootImpl; import android.view.ViewStub; -import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; @@ -113,8 +104,6 @@ import android.widget.TextView; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; /** * Android-specific Window. @@ -213,6 +202,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private int mFrameResource = 0; private int mTextColor = 0; + private int mStatusBarColor = 0; + private int mNavigationBarColor = 0; private CharSequence mTitle = null; @@ -1987,7 +1978,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } - private final class DecorView extends FrameLayout implements RootViewSurfaceTaker { + private final class DecorView extends FrameLayout implements RootViewSurfaceTaker, + View.OnSystemUiVisibilityChangeListener { /* package */int mDefaultOpacity = PixelFormat.OPAQUE; /** The feature ID of the panel, or -1 if this is the application's DecorView */ @@ -2017,6 +2009,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // View added at runtime to draw under the navigation bar area private View mNavigationGuard; + private View mStatusColorView; + private View mNavigationColorView; + + private int mLastTopInset = 0; + private int mLastBottomInset = 0; + private int mLastSystemUiVisibility = 0; + + public DecorView(Context context, int featureId) { super(context); mFeatureId = featureId; @@ -2582,8 +2582,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override + public void onSystemUiVisibilityChange(int visible) { + mLastSystemUiVisibility = visible; + updateColorViews(null /* insets */); + } + + @Override protected boolean fitSystemWindows(Rect insets) { mFrameOffsets.set(insets); + updateColorViews(insets); updateStatusGuard(insets); updateNavigationGuard(insets); if (getForeground() != null) { @@ -2597,6 +2604,52 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return false; } + private void updateColorViews(Rect insets) { + if (mIsFloating || !ActivityManager.isHighEndGfx()) { + // No colors on floating windows or low end devices :( + return; + } + if (insets != null) { + mLastTopInset = insets.top; + mLastBottomInset = insets.bottom; + } + mStatusColorView = updateColorViewInt(mStatusColorView, + SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, + mStatusBarColor, mLastTopInset, Gravity.TOP); + mNavigationColorView = updateColorViewInt(mNavigationColorView, + SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, + mNavigationBarColor, mLastBottomInset, Gravity.BOTTOM); + } + + private View updateColorViewInt(View view, int systemUiHideFlag, int translucentFlag, + int color, int height, int verticalGravity) { + boolean show = height > 0 && (mLastSystemUiVisibility & systemUiHideFlag) == 0 + && (getAttributes().flags & translucentFlag) == 0 + && (color & Color.BLACK) != 0 + && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; + + if (view == null) { + if (show) { + view = new View(mContext); + view.setBackgroundColor(color); + addView(view, new LayoutParams(LayoutParams.MATCH_PARENT, height, + Gravity.START | verticalGravity)); + } + } else { + int vis = show ? VISIBLE : INVISIBLE; + view.setVisibility(vis); + if (show) { + LayoutParams lp = (LayoutParams) view.getLayoutParams(); + if (lp.height != height) { + lp.height = height; + view.setLayoutParams(lp); + } + view.setBackgroundColor(color); + } + } + return view; + } + private void updateStatusGuard(Rect insets) { boolean showStatusGuard = false; // Show the status guard when the non-overlay contextual action bar is showing @@ -2616,9 +2669,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mStatusGuard = new View(mContext); mStatusGuard.setBackgroundColor(mContext.getResources() .getColor(R.color.input_method_navigation_guard)); - addView(mStatusGuard, new LayoutParams( - LayoutParams.MATCH_PARENT, mlp.topMargin, - Gravity.START | Gravity.TOP)); + addView(mStatusGuard, indexOfChild(mStatusColorView), + new LayoutParams(LayoutParams.MATCH_PARENT, mlp.topMargin, + Gravity.START | Gravity.TOP)); } else { LayoutParams lp = (LayoutParams) mStatusGuard.getLayoutParams(); if (lp.height != mlp.topMargin) { @@ -2663,7 +2716,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mNavigationGuard = new View(mContext); mNavigationGuard.setBackgroundColor(mContext.getResources() .getColor(R.color.input_method_navigation_guard)); - addView(mNavigationGuard, new LayoutParams( + addView(mNavigationGuard, indexOfChild(mNavigationColorView), new LayoutParams( LayoutParams.MATCH_PARENT, insets.bottom, Gravity.START | Gravity.BOTTOM)); } else { @@ -3009,6 +3062,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final int targetSdk = context.getApplicationInfo().targetSdkVersion; final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; + final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.L; final boolean targetHcNeedsOptions = context.getResources().getBoolean( com.android.internal.R.bool.target_honeycomb_needs_options_menu); final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE); @@ -3018,7 +3072,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } else { clearFlags(WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY); } - + + // Non-floating windows on high end devices must put up decor beneath the system bars and + // therefore must know about visibility changes of those. + if (!mIsFloating && ActivityManager.isHighEndGfx()) { + if (!targetPreL && a.getBoolean( + com.android.internal.R.styleable.Window_windowDrawsSystemBarBackgrounds, + false)) { + setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags()); + } + decor.setOnSystemUiVisibilityChangeListener(decor); + } + mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000); + mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000); + if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { if (a.getBoolean( @@ -3522,6 +3590,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return (mLeftIconView = (ImageView)findViewById(com.android.internal.R.id.left_icon)); } + @Override + protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) { + super.dispatchWindowAttributesChanged(attrs); + if (mDecor != null) { + mDecor.updateColorViews(null /* insets */); + } + } + private ProgressBar getCircularProgressBar(boolean shouldInstallDecor) { if (mCircularProgressBar != null) { return mCircularProgressBar; @@ -4166,4 +4242,22 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { void sendCloseSystemWindows(String reason) { PhoneWindowManager.sendCloseSystemWindows(getContext(), reason); } + + @Override + public int getStatusBarColor() { + return 0; + } + + @Override + public void setStatusBarColor(int color) { + } + + @Override + public int getNavigationBarColor() { + return 0; + } + + @Override + public void setNavigationBarColor(int color) { + } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 4ee8103..8eed414 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -170,7 +170,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.STATUS_BAR_TRANSLUCENT - | View.NAVIGATION_BAR_TRANSLUCENT; + | View.NAVIGATION_BAR_TRANSLUCENT + | View.SYSTEM_UI_TRANSPARENT; /** * Keyguard stuff @@ -1368,6 +1369,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { // The status bar is the only window allowed to exhibit keyguard behavior. attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; } + + if (ActivityManager.isHighEndGfx() + && (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { + attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + } } void readLidState() { @@ -2708,7 +2716,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // drive nav being hidden only by whether it is requested. final int sysui = mLastSystemUiFlags; boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; - boolean navTranslucent = (sysui & View.NAVIGATION_BAR_TRANSLUCENT) != 0; + boolean navTranslucent = (sysui + & (View.NAVIGATION_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0; boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0; boolean navAllowedHidden = immersive || immersiveSticky; @@ -2834,7 +2843,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mStableTop = mUnrestrictedScreenTop + mStatusBarHeight; boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0; - boolean statusBarTranslucent = (sysui & View.STATUS_BAR_TRANSLUCENT) != 0; + boolean statusBarTranslucent = (sysui + & (View.STATUS_BAR_TRANSLUCENT | View.SYSTEM_UI_TRANSPARENT)) != 0; statusBarTranslucent &= areTranslucentBarsAllowed(); // If the status bar is hidden, we don't want to cause @@ -3030,12 +3040,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (isAppWindow && !inheritTranslucentDecor && !topAtRest) { if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 && (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0 - && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0) { + && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0 + && (fl & WindowManager.LayoutParams. + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { // Ensure policy decor includes status bar dcf.top = mStableTop; } if ((fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0 - && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) { + && (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 + && (fl & WindowManager.LayoutParams. + FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { // Ensure policy decor includes navigation bar dcf.bottom = mStableBottom; dcf.right = mStableRight; @@ -5232,7 +5246,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (!areTranslucentBarsAllowed()) { - vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT); + vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT + | View.SYSTEM_UI_TRANSPARENT); } // update status bar diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index e26747c..be20616 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -36,12 +36,14 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.media.AudioService; import android.os.AsyncTask; import android.os.Binder; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.os.UserManager; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -49,6 +51,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.Xml; @@ -56,6 +59,7 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsCallback; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; +import com.google.android.util.AbstractMessageParser.MusicTrack; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -91,6 +95,10 @@ public class AppOpsService extends IAppOpsService.Stub { final SparseArray<HashMap<String, Ops>> mUidOps = new SparseArray<HashMap<String, Ops>>(); + private int mDeviceOwnerUid; + private final SparseIntArray mProfileOwnerUids = new SparseIntArray(); + private final SparseArray<boolean[]> mOpRestrictions = new SparseArray<boolean[]>(); + public final static class Ops extends SparseArray<Op> { public final String packageName; public final int uid; @@ -548,6 +556,9 @@ public class AppOpsService extends IAppOpsService.Stub { verifyIncomingUid(uid); verifyIncomingOp(code); synchronized (this) { + if (isOpRestricted(uid, code)) { + return AppOpsManager.MODE_IGNORED; + } Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false); if (op == null) { return AppOpsManager.opToDefaultMode(code); @@ -631,6 +642,9 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_ERRORED; } Op op = getOpLocked(ops, code, true); + if (isOpRestricted(uid, code)) { + return AppOpsManager.MODE_IGNORED; + } if (op.duration == -1) { Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code " + code + " time=" + op.time + " duration=" + op.duration); @@ -665,6 +679,9 @@ public class AppOpsService extends IAppOpsService.Stub { return AppOpsManager.MODE_ERRORED; } Op op = getOpLocked(ops, code, true); + if (isOpRestricted(uid, code)) { + return AppOpsManager.MODE_IGNORED; + } final int switchCode = AppOpsManager.opToSwitch(code); final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { @@ -830,6 +847,23 @@ public class AppOpsService extends IAppOpsService.Stub { return op; } + private boolean isOpRestricted(int uid, int code) { + int userHandle = UserHandle.getUserId(uid); + boolean[] opRestrictions = mOpRestrictions.get(userHandle); + if ((opRestrictions != null) && opRestrictions[code]) { + if (userHandle == UserHandle.USER_OWNER) { + if (uid != mDeviceOwnerUid) { + return true; + } + } else { + if (uid != mProfileOwnerUids.get(userHandle, -1)) { + return true; + } + } + } + return false; + } + void readState() { synchronized (mFile) { synchronized (this) { @@ -1167,4 +1201,66 @@ public class AppOpsService extends IAppOpsService.Stub { int mode; ArraySet<String> exceptionPackages = NO_EXCEPTIONS; } + + @Override + public void setDeviceOwner(String packageName) throws RemoteException { + checkSystemUid("setDeviceOwner"); + try { + mDeviceOwnerUid = mContext.getPackageManager().getPackageUid(packageName, + UserHandle.USER_OWNER); + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not find Device Owner UID"); + mDeviceOwnerUid = -1; + throw new IllegalArgumentException("Could not find device owner package " + + packageName); + } + } + + @Override + public void setProfileOwner(String packageName, int userHandle) throws RemoteException { + checkSystemUid("setProfileOwner"); + try { + int uid = mContext.getPackageManager().getPackageUid(packageName, + userHandle); + mProfileOwnerUids.put(userHandle, uid); + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not find Profile Owner UID"); + mProfileOwnerUids.put(userHandle, -1); + throw new IllegalArgumentException("Could not find profile owner package " + + packageName); + } + } + + @Override + public void setUserRestrictions(Bundle restrictions, int userHandle) throws RemoteException { + checkSystemUid("setUserRestrictions"); + boolean[] opRestrictions = mOpRestrictions.get(userHandle); + if (opRestrictions == null) { + opRestrictions = new boolean[AppOpsManager._NUM_OP]; + mOpRestrictions.put(userHandle, opRestrictions); + } + for (int i = 0; i < opRestrictions.length; ++i) { + String restriction = AppOpsManager.opToRestriction(i); + if (restriction != null) { + opRestrictions[i] = restrictions.getBoolean(restriction, false); + } else { + opRestrictions[i] = false; + } + } + } + + @Override + public void removeUser(int userHandle) throws RemoteException { + checkSystemUid("removeUser"); + mOpRestrictions.remove(userHandle); + mProfileOwnerUids.removeAt(mProfileOwnerUids.indexOfKey(userHandle)); + } + + private void checkSystemUid(String function) { + int uid = Binder.getCallingUid(); + if (uid != Process.SYSTEM_UID) { + throw new SecurityException(function + " must by called by the system"); + } + } + } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 0708e55..2d0f6d1 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3216,6 +3216,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // tell the network currently servicing this that it's no longer interested NetworkAgentInfo affectedNetwork = mNetworkForRequestId.get(nri.request.requestId); if (affectedNetwork != null) { + mNetworkForRequestId.remove(nri.request.requestId); affectedNetwork.networkRequests.remove(nri.request.requestId); if (VDBG) { log(" Removing from current network " + affectedNetwork.name() + ", leaving " + @@ -5491,10 +5492,29 @@ public class ConnectivityService extends IConnectivityManager.Stub { } catch (Exception e) { loge("Exception in setDnsServersForNetwork: " + e); } - // TODO - setprop "net.dnsX" + NetworkAgentInfo defaultNai = mNetworkForRequestId.get(mDefaultRequest.requestId); + if (defaultNai != null && defaultNai.network.netId == netId) { + setDefaultDnsSystemProperties(dnses); + } } } + private void setDefaultDnsSystemProperties(Collection<InetAddress> dnses) { + int last = 0; + for (InetAddress dns : dnses) { + ++last; + String key = "net.dns" + last; + String value = dns.getHostAddress(); + SystemProperties.set(key, value); + } + for (int i = last + 1; i <= mNumDnsEntries; ++i) { + String key = "net.dns" + i; + SystemProperties.set(key, ""); + } + mNumDnsEntries = last; + } + + private void updateCapabilities(NetworkAgentInfo networkAgent, NetworkCapabilities networkCapabilities) { // TODO - what else here? Verify still satisfies everybody? @@ -5610,6 +5630,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (mDefaultRequest.requestId == nri.request.requestId) { isNewDefault = true; updateActiveDefaultNetwork(newNetwork); + if (newNetwork.linkProperties != null) { + setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnses()); + } else { + setDefaultDnsSystemProperties(new ArrayList<InetAddress>()); + } } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e7e2f4d..bab5b9c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -32,6 +32,7 @@ import android.Manifest; import android.app.AppOpsManager; import android.app.IActivityContainer; import android.app.IActivityContainerCallback; +import android.app.IAppTask; import android.appwidget.AppWidgetManager; import android.graphics.Rect; import android.os.BatteryStats; @@ -2919,7 +2920,7 @@ public final class ActivityManagerService extends ActivityManagerNative debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } - String requiredAbi = app.info.requiredCpuAbi; + String requiredAbi = app.info.cpuAbi; if (requiredAbi == null) { requiredAbi = Build.SUPPORTED_ABIS[0]; } @@ -7019,6 +7020,33 @@ public final class ActivityManagerService extends ActivityManagerNative // ========================================================= @Override + public List<IAppTask> getAppTasks() { + int callingUid = Binder.getCallingUid(); + long ident = Binder.clearCallingIdentity(); + synchronized(this) { + ArrayList<IAppTask> list = new ArrayList<IAppTask>(); + try { + if (localLOGV) Slog.v(TAG, "getAppTasks"); + + final int N = mRecentTasks.size(); + for (int i = 0; i < N; i++) { + TaskRecord tr = mRecentTasks.get(i); + // Skip tasks that are not created by the caller + if (tr.creatorUid == callingUid) { + ActivityManager.RecentTaskInfo taskInfo = + createRecentTaskInfoFromTaskRecord(tr); + AppTaskImpl taskImpl = new AppTaskImpl(taskInfo.persistentId, callingUid); + list.add(taskImpl); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + return list; + } + } + + @Override public List<RunningTaskInfo> getTasks(int maxNum, int flags) { final int callingUid = Binder.getCallingUid(); ArrayList<RunningTaskInfo> list = new ArrayList<RunningTaskInfo>(); @@ -7046,6 +7074,66 @@ public final class ActivityManagerService extends ActivityManagerNative return mRecentTasks.get(0); } + /** + * Creates a new RecentTaskInfo from a TaskRecord. + */ + private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) { + ActivityManager.RecentTaskInfo rti + = new ActivityManager.RecentTaskInfo(); + rti.id = tr.numActivities > 0 ? tr.taskId : -1; + rti.persistentId = tr.taskId; + rti.baseIntent = new Intent(tr.getBaseIntent()); + rti.origActivity = tr.origActivity; + rti.description = tr.lastDescription; + rti.stackId = tr.stack.mStackId; + rti.userId = tr.userId; + + // Traverse upwards looking for any break between main task activities and + // utility activities. + final ArrayList<ActivityRecord> activities = tr.mActivities; + int activityNdx; + final int numActivities = activities.size(); + for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; + ++activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.intent != null && + (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) + != 0) { + break; + } + } + if (activityNdx > 0) { + // Traverse downwards starting below break looking for set label, icon. + // Note that if there are activities in the task but none of them set the + // recent activity values, then we do not fall back to the last set + // values in the TaskRecord. + rti.activityValues = new ActivityManager.RecentsActivityValues(); + for (--activityNdx; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.activityValues != null) { + if (rti.activityValues.label == null) { + rti.activityValues.label = r.activityValues.label; + tr.lastActivityValues.label = r.activityValues.label; + } + if (rti.activityValues.icon == null) { + rti.activityValues.icon = r.activityValues.icon; + tr.lastActivityValues.icon = r.activityValues.icon; + } + if (rti.activityValues.colorPrimary == 0) { + rti.activityValues.colorPrimary = r.activityValues.colorPrimary; + tr.lastActivityValues.colorPrimary = r.activityValues.colorPrimary; + } + } + } + } else { + // If there are no activity records in this task, then we use the last + // resolved values + rti.activityValues = + new ActivityManager.RecentsActivityValues(tr.lastActivityValues); + } + return rti; + } + @Override public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) { @@ -7102,63 +7190,11 @@ public final class ActivityManagerService extends ActivityManagerNative continue; } } - ActivityManager.RecentTaskInfo rti - = new ActivityManager.RecentTaskInfo(); - rti.id = tr.numActivities > 0 ? tr.taskId : -1; - rti.persistentId = tr.taskId; - rti.baseIntent = new Intent( - tr.intent != null ? tr.intent : tr.affinityIntent); + + ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr); if (!detailed) { rti.baseIntent.replaceExtras((Bundle)null); } - rti.origActivity = tr.origActivity; - rti.description = tr.lastDescription; - rti.stackId = tr.stack.mStackId; - rti.userId = tr.userId; - - // Traverse upwards looking for any break between main task activities and - // utility activities. - final ArrayList<ActivityRecord> activities = tr.mActivities; - int activityNdx; - final int numActivities = activities.size(); - for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; - ++activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - if (r.intent != null && - (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) - != 0) { - break; - } - } - if (activityNdx > 0) { - // Traverse downwards starting below break looking for set label, icon. - // Note that if there are activities in the task but none of them set the - // recent activity values, then we do not fall back to the last set - // values in the TaskRecord. - rti.activityValues = new ActivityManager.RecentsActivityValues(); - for (--activityNdx; activityNdx >= 0; --activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - if (r.activityValues != null) { - if (rti.activityValues.label == null) { - rti.activityValues.label = r.activityValues.label; - tr.lastActivityValues.label = r.activityValues.label; - } - if (rti.activityValues.icon == null) { - rti.activityValues.icon = r.activityValues.icon; - tr.lastActivityValues.icon = r.activityValues.icon; - } - if (rti.activityValues.colorPrimary == 0) { - rti.activityValues.colorPrimary = r.activityValues.colorPrimary; - tr.lastActivityValues.colorPrimary = r.activityValues.colorPrimary; - } - } - } - } else { - // If there are no activity records in this task, then we use the last - // resolved values - rti.activityValues = - new ActivityManager.RecentsActivityValues(tr.lastActivityValues); - } if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) { // Check whether this activity is currently available. @@ -17174,4 +17210,69 @@ public final class ActivityManagerService extends ActivityManagerNative ActivityManagerService.this.wakingUp(); } } + + /** + * An implementation of IAppTask, that allows an app to manage its own tasks via + * {@link android.app.ActivityManager#AppTask}. We keep track of the callingUid to ensure that + * only the process that calls getAppTasks() can call the AppTask methods. + */ + class AppTaskImpl extends IAppTask.Stub { + private int mTaskId; + private int mCallingUid; + + public AppTaskImpl(int taskId, int callingUid) { + mTaskId = taskId; + mCallingUid = callingUid; + } + + @Override + public void finishAndRemoveTask() { + // Ensure that we are called from the same process that created this AppTask + if (mCallingUid != Binder.getCallingUid()) { + Slog.w(TAG, "finishAndRemoveTask: caller " + mCallingUid + + " does not match caller of getAppTasks(): " + Binder.getCallingUid()); + return; + } + + synchronized (ActivityManagerService.this) { + long origId = Binder.clearCallingIdentity(); + try { + TaskRecord tr = recentTaskForIdLocked(mTaskId); + if (tr != null) { + // Only kill the process if we are not a new document + int flags = tr.getBaseIntent().getFlags(); + boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == + Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + removeTaskByIdLocked(mTaskId, + !isDocument ? ActivityManager.REMOVE_TASK_KILL_PROCESS : 0); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + } + + @Override + public ActivityManager.RecentTaskInfo getTaskInfo() { + // Ensure that we are called from the same process that created this AppTask + if (mCallingUid != Binder.getCallingUid()) { + Slog.w(TAG, "finishAndRemoveTask: caller " + mCallingUid + + " does not match caller of getAppTasks(): " + Binder.getCallingUid()); + return null; + } + + synchronized (ActivityManagerService.this) { + long origId = Binder.clearCallingIdentity(); + try { + TaskRecord tr = recentTaskForIdLocked(mTaskId); + if (tr != null) { + return createRecentTaskInfoFromTaskRecord(tr); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + return null; + } + } + } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index dd12a65..908bfbd 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -27,6 +27,7 @@ import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Parcel; +import android.os.PowerManagerInternal; import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; @@ -40,6 +41,7 @@ import android.util.Slog; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.PowerProfile; +import com.android.server.LocalServices; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -49,7 +51,8 @@ import java.util.List; * All information we are collecting about things that can happen that impact * battery life. */ -public final class BatteryStatsService extends IBatteryStats.Stub { +public final class BatteryStatsService extends IBatteryStats.Stub + implements PowerManagerInternal.LowPowerModeListener { static final String TAG = "BatteryStatsService"; static IBatteryStats sService; @@ -58,6 +61,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub { Context mContext; private boolean mBluetoothPendingStats; private BluetoothHeadset mBluetoothHeadset; + PowerManagerInternal mPowerManagerInternal; BatteryStatsService(String filename, Handler handler) { mStats = new BatteryStatsImpl(filename, handler); @@ -70,6 +74,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub { mStats.setRadioScanningTimeout(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); + mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); + mPowerManagerInternal.registerLowPowerModeObserver(this); + mStats.noteLowPowerMode(mPowerManagerInternal.getLowPowerModeEnabled()); (new WakeupReasonThread()).start(); } @@ -88,7 +95,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub { sService = asInterface(b); return sService; } - + + @Override + public void onLowPowerModeChanged(boolean enabled) { + synchronized (mStats) { + mStats.noteLowPowerMode(enabled); + } + } + /** * @return the current statistics object, which may be modified * to reflect events that affect battery usage. You must lock the @@ -154,11 +168,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } - public void noteStopWakelock(int uid, int pid, String name, int type) { + public void noteStopWakelock(int uid, int pid, String name, String historyName, int type) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteStopWakeLocked(uid, pid, name, type, SystemClock.elapsedRealtime(), - SystemClock.uptimeMillis()); + mStats.noteStopWakeLocked(uid, pid, name, historyName, type, + SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); } } @@ -171,20 +185,21 @@ public final class BatteryStatsService extends IBatteryStats.Stub { } } - public void noteChangeWakelockFromSource(WorkSource ws, int pid, String name, int type, - WorkSource newWs, int newPid, String newName, + public void noteChangeWakelockFromSource(WorkSource ws, int pid, String name, + String historyName, int type, WorkSource newWs, int newPid, String newName, String newHistoryName, int newType, boolean newUnimportantForLogging) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, type, + mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type, newWs, newPid, newName, newHistoryName, newType, newUnimportantForLogging); } } - public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, int type) { + public void noteStopWakelockFromSource(WorkSource ws, int pid, String name, String historyName, + int type) { enforceCallingPermission(); synchronized (mStats) { - mStats.noteStopWakeFromSourceLocked(ws, pid, name, type); + mStats.noteStopWakeFromSourceLocked(ws, pid, name, historyName, type); } } @@ -608,10 +623,31 @@ public final class BatteryStatsService extends IBatteryStats.Stub { pw.println(" --charged: only output data since last charged."); pw.println(" --reset: reset the stats, clearing all current data."); pw.println(" --write: force write current collected stats to disk."); + pw.println(" --enable: enable an option: full-wake-history."); + pw.println(" --disable: disable an option: full-wake-history."); pw.println(" -h: print this help text."); pw.println(" <package.name>: optional name of package to filter output by."); } + private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) { + i++; + if (i >= args.length) { + pw.println("Missing option argument for " + (enable ? "--enable" : "--disable")); + dumpHelp(pw); + return -1; + } + if ("full-wake-history".equals(args[i])) { + synchronized (mStats) { + mStats.setRecordAllWakeLocksLocked(enable); + } + } else { + pw.println("Unknown enable/disable option: " + args[i]); + dumpHelp(pw); + return -1; + } + return i; + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -662,6 +698,20 @@ public final class BatteryStatsService extends IBatteryStats.Stub { pw.println("Battery stats written."); noOutput = true; } + } else if ("--enable".equals(arg)) { + i = doEnableOrDisable(pw, i, args, true); + if (i < 0) { + return; + } + pw.println("Enabled: " + args[i]); + return; + } else if ("--disable".equals(arg)) { + i = doEnableOrDisable(pw, i, args, false); + if (i < 0) { + return; + } + pw.println("Disabled: " + args[i]); + return; } else if ("-h".equals(arg)) { dumpHelp(pw); return; diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 862932c..be884e7 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -154,6 +154,11 @@ final class TaskRecord extends ThumbnailHolder { } } + /** Returns the intent for the root activity for this task */ + Intent getBaseIntent() { + return intent != null ? intent : affinityIntent; + } + /** Returns the first non-finishing activity from the root. */ ActivityRecord getRootActivity() { for (int i = 0; i < mActivities.size(); i++) { diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java index 296cc5b..d747cd0 100644 --- a/services/core/java/com/android/server/hdmi/FeatureAction.java +++ b/services/core/java/com/android/server/hdmi/FeatureAction.java @@ -15,7 +15,6 @@ */ package com.android.server.hdmi; -import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; import android.os.Handler; import android.os.Looper; @@ -155,23 +154,10 @@ abstract class FeatureAction { mActionTimer.sendTimerMessage(state, delayMillis); } - static HdmiCecMessage buildCommand(int src, int dst, int opcode, byte[] params) { - return new HdmiCecMessage(src, dst, opcode, params); - } - - // Build a CEC command that does not have parameter. - static HdmiCecMessage buildCommand(int src, int dst, int opcode) { - return new HdmiCecMessage(src, dst, opcode, HdmiCecMessage.EMPTY_PARAM); - } - protected final void sendCommand(HdmiCecMessage cmd) { mService.sendCecCommand(cmd); } - protected final void sendBroadcastCommand(int opcode, byte[] param) { - sendCommand(buildCommand(mSourceAddress, HdmiCec.ADDR_BROADCAST, opcode, param)); - } - /** * Finish up the action. Reset the state, and remove itself from the action queue. */ @@ -189,23 +175,4 @@ abstract class FeatureAction { private void removeAction(FeatureAction action) { mService.removeAction(action); } - - // Utility methods for generating parameter byte arrays for CEC commands. - protected static byte[] uiCommandParam(int uiCommand) { - return new byte[] {(byte) uiCommand}; - } - - protected static byte[] physicalAddressParam(int physicalAddress) { - return new byte[] { - (byte) ((physicalAddress >> 8) & 0xFF), - (byte) (physicalAddress & 0xFF) - }; - } - - protected static byte[] pathPairParam(int oldPath, int newPath) { - return new byte[] { - (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF), - (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF) - }; - } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index b103a4d..5327ef4 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -55,15 +55,6 @@ final class HdmiCecController { // A message to report allocated logical address to main control looper. private final static int MSG_REPORT_LOGICAL_ADDRESS = 2; - // TODO: move these values to HdmiCec.java once make it internal constant class. - // CEC's ABORT reason values. - private static final int ABORT_UNRECOGNIZED_MODE = 0; - private static final int ABORT_NOT_IN_CORRECT_MODE = 1; - private static final int ABORT_CANNOT_PROVIDE_SOURCE = 2; - private static final int ABORT_INVALID_OPERAND = 3; - private static final int ABORT_REFUSED = 4; - private static final int ABORT_UNABLE_TO_DETERMINE = 5; - private static final int NUM_LOGICAL_ADDRESS = 16; // TODO: define other constants for errors. @@ -80,10 +71,16 @@ final class HdmiCecController { // interacts with HAL. private long mNativePtr; + private HdmiControlService mService; + // Map-like container of all cec devices. A logical address of device is // used as key of container. private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<HdmiCecDeviceInfo>(); + // Set-like container for all local devices' logical address. + // Key and value are same. + private final SparseArray<Integer> mLocalLogicalAddresses = + new SparseArray<Integer>(); // Private constructor. Use HdmiCecController.create(). private HdmiCecController() { @@ -325,6 +322,7 @@ final class HdmiCecController { */ int addLogicalAddress(int newLogicalAddress) { if (HdmiCec.isValidAddress(newLogicalAddress)) { + mLocalLogicalAddresses.append(newLogicalAddress, newLogicalAddress); return nativeAddLogicalAddress(mNativePtr, newLogicalAddress); } else { return -1; @@ -337,6 +335,9 @@ final class HdmiCecController { * <p>Declared as package-private. accessed by {@link HdmiControlService} only. */ void clearLogicalAddress() { + // TODO: consider to backup logical address so that new logical address + // allocation can use it as preferred address. + mLocalLogicalAddresses.clear(); nativeClearLogicalAddress(mNativePtr); } @@ -371,30 +372,37 @@ final class HdmiCecController { } private void init(HdmiControlService service, long nativePtr) { + mService = service; mIoHandler = new IoHandler(service.getServiceLooper()); mControlHandler = new ControlHandler(service.getServiceLooper()); mNativePtr = nativePtr; } + private boolean isAcceptableAddress(int address) { + // Can access command targeting devices available in local device or + // broadcast command. + return address == HdmiCec.ADDR_BROADCAST + || mLocalLogicalAddresses.get(address) != null; + } + private void onReceiveCommand(HdmiCecMessage message) { - // TODO: Handle message according to opcode type. + if (isAcceptableAddress(message.getDestination()) && + mService.handleCecCommand(message)) { + return; + } // TODO: Use device's source address for broadcast message. int sourceAddress = message.getDestination() != HdmiCec.ADDR_BROADCAST ? message.getDestination() : 0; // Reply <Feature Abort> to initiator (source) for all requests. - sendFeatureAbort(sourceAddress, message.getSource(), message.getOpcode(), - ABORT_REFUSED); - } + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand + (sourceAddress, message.getSource(), message.getOpcode(), + HdmiCecMessageBuilder.ABORT_REFUSED); + sendCommand(cecMessage); - private void sendFeatureAbort(int srcAddress, int destAddress, int originalOpcode, - int reason) { - byte[] params = new byte[2]; - params[0] = (byte) originalOpcode; - params[1] = (byte) reason; + } - HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, destAddress, - HdmiCec.MESSAGE_FEATURE_ABORT, params); + void sendCommand(HdmiCecMessage cecMessage) { Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage); mIoHandler.sendMessage(message); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java new file mode 100644 index 0000000..6d2b83b --- /dev/null +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java @@ -0,0 +1,212 @@ +/* + * 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.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; + +import java.io.UnsupportedEncodingException; + +/** + * A helper class to build {@link HdmiCecMessage} from various cec commands. + */ +public class HdmiCecMessageBuilder { + // TODO: move these values to HdmiCec.java once make it internal constant class. + // CEC's ABORT reason values. + static final int ABORT_UNRECOGNIZED_MODE = 0; + static final int ABORT_NOT_IN_CORRECT_MODE = 1; + static final int ABORT_CANNOT_PROVIDE_SOURCE = 2; + static final int ABORT_INVALID_OPERAND = 3; + static final int ABORT_REFUSED = 4; + static final int ABORT_UNABLE_TO_DETERMINE = 5; + + private static final int OSD_NAME_MAX_LENGTH = 13; + + private HdmiCecMessageBuilder() {} + + /** + * Build <Feature Abort> command. <Feature Abort> consists of + * 1 byte original opcode and 1 byte reason fields with basic fields. + * + * @param src source address of command + * @param dest destination address of command + * @param originalOpcode original opcode causing feature abort + * @param reason reason of feature abort + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildFeatureAbortCommand(int src, int dest, int originalOpcode, + int reason) { + byte[] params = new byte[] { + (byte) originalOpcode, + (byte) reason, + }; + return buildCommand(src, dest, HdmiCec.MESSAGE_FEATURE_ABORT, params); + } + + /** + * Build <Give Osd Name> command. + * + * @param src source address of command + * @param dest destination address of command + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildGiveOsdNameCommand(int src, int dest) { + return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_OSD_NAME); + } + + /** + * Build <Give Vendor Id Command> command. + * + * @param src source address of command + * @param dest destination address of command + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildGiveDeviceVendorIdCommand(int src, int dest) { + return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID); + } + + /** + * Build <Set Menu Language > command. + * + * <p>This is a broadcast message sent to all devices on the bus. + * + * @param src source address of command + * @param language 3-letter ISO639-2 based language code + * @return newly created {@link HdmiCecMessage} if language is valid. + * Otherwise, return null + */ + static HdmiCecMessage buildSetMenuLanguageCommand(int src, String language) { + if (language.length() != 3) { + return null; + } + // Hdmi CEC uses lower-cased ISO 639-2 (3 letters code). + String normalized = language.toLowerCase(); + byte[] params = new byte[] { + (byte) normalized.charAt(0), + (byte) normalized.charAt(1), + (byte) normalized.charAt(2), + }; + // <Set Menu Language> is broadcast message. + return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_SET_MENU_LANGUAGE, + params); + } + + /** + * Build <Set Osd Name > command. + * + * @param src source address of command + * @param name display (OSD) name of device + * @return newly created {@link HdmiCecMessage} if valid name. Otherwise, + * return null + */ + static HdmiCecMessage buildSetOsdNameCommand(int src, int dest, String name) { + int length = Math.min(name.length(), OSD_NAME_MAX_LENGTH); + byte[] params; + try { + params = name.substring(0, length).getBytes("US-ASCII"); + } catch (UnsupportedEncodingException e) { + return null; + } + return buildCommand(src, dest, HdmiCec.MESSAGE_SET_OSD_NAME, params); + } + + /** + * Build <Report Physical Address> command. It has two bytes physical + * address and one byte device type as parameter. + * + * <p>This is a broadcast message sent to all devices on the bus. + * + * @param src source address of command + * @param address physical address of device + * @param deviceType type of device + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildReportPhysicalAddressCommand(int src, int address, int deviceType) { + byte[] params = new byte[] { + // Two bytes for physical address + (byte) ((address >> 8) & 0xFF), + (byte) (address & 0xFF), + // One byte device type + (byte) deviceType + }; + // <Report Physical Address> is broadcast message. + return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS, + params); + } + + /** + * Build <Device Vendor Id> command. It has three bytes vendor id as + * parameter. + * + * <p>This is a broadcast message sent to all devices on the bus. + * + * @param src source address of command + * @param vendorId device's vendor id + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildDeviceVendorIdCommand(int src, int vendorId) { + byte[] params = new byte[] { + (byte) ((vendorId >> 16) & 0xFF), + (byte) ((vendorId >> 8) & 0xFF), + (byte) (vendorId & 0xFF) + }; + // <Device Vendor Id> is broadcast message. + return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_DEVICE_VENDOR_ID, + params); + } + + /** + * Build <Device Vendor Id> command. It has one byte cec version as parameter. + * + * @param src source address of command + * @param dest destination address of command + * @param version version of cec. Use 0x04 for "Version 1.3a" and 0x05 for + * "Version 1.4 or 1.4a or 1.4b + * @return newly created {@link HdmiCecMessage} + */ + static HdmiCecMessage buildCecVersion(int src, int dest, int version) { + byte[] params = new byte[] { + (byte) version + }; + return buildCommand(src, dest, HdmiCec.MESSAGE_CEC_VERSION, params); + } + + /** + * Build a {@link HdmiCecMessage} without extra parameter. + * + * @param src source address of command + * @param dest destination address of command + * @param opcode opcode for a message + * @return newly created {@link HdmiCecMessage} + */ + private static HdmiCecMessage buildCommand(int src, int dest, int opcode) { + return new HdmiCecMessage(src, dest, opcode, HdmiCecMessage.EMPTY_PARAM); + } + + /** + * Build a {@link HdmiCecMessage} with given values. + * + * @param src source address of command + * @param dest destination address of command + * @param opcode opcode for a message + * @param params extra parameters for command + * @return newly created {@link HdmiCecMessage} + */ + private static HdmiCecMessage buildCommand(int src, int dest, int opcode, byte[] params) { + return new HdmiCecMessage(src, dest, opcode, params); + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index f99c717..ed48c12 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -18,6 +18,7 @@ package com.android.server.hdmi; import android.annotation.Nullable; import android.content.Context; +import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; import android.hardware.hdmi.HdmiCecMessage; import android.os.HandlerThread; @@ -26,6 +27,8 @@ import android.util.Slog; import com.android.server.SystemService; +import java.util.Locale; + /** * Provides a service for sending and processing HDMI control messages, * HDMI-CEC and MHL control command, and providing the information on both standard. @@ -105,7 +108,7 @@ public final class HdmiControlService extends SystemService { * @param command CEC command to send out */ void sendCecCommand(HdmiCecMessage command) { - // TODO: Implement this. + mCecController.sendCommand(command); } /** @@ -116,4 +119,89 @@ public final class HdmiControlService extends SystemService { void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) { // TODO: Implement this. } + + boolean handleCecCommand(HdmiCecMessage message) { + // Commands that queries system information replies directly instead + // of creating FeatureAction because they are state-less. + 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; + // TODO: Add remaining system information query such as + // <Give Device Power Status> and <Request Active Source> handler. + default: + Slog.w(TAG, "Unsupported cec command:" + message.toString()); + return false; + } + } + + 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, + HdmiCecMessageBuilder.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()); + } + } } diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index c84a067..e0bc718 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -71,7 +71,8 @@ final class NewDeviceAction extends FeatureAction { @Override public boolean start() { sendCommand( - buildCommand(mSourceAddress, mDeviceLogicalAddress, HdmiCec.MESSAGE_GET_OSD_NAME)); + HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, + mDeviceLogicalAddress)); mState = STATE_WAITING_FOR_SET_OSD_NAME; addTimer(mState, TIMEOUT_MS); return true; @@ -132,8 +133,8 @@ final class NewDeviceAction extends FeatureAction { } private void requestVendorId() { - sendCommand(buildCommand(mSourceAddress, mDeviceLogicalAddress, - HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID)); + sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, + mDeviceLogicalAddress)); addTimer(mState, TIMEOUT_MS); } diff --git a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java index c4e2058..1c5cacd 100644 --- a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java @@ -58,12 +58,16 @@ public class MediaRouteProviderProxy { private final int mUserId; // Interfaces declared in the manifest private final ArrayList<String> mInterfaces = new ArrayList<String>(); - private final ArrayList<RouteConnectionRecord> mConnections = new ArrayList<RouteConnectionRecord>(); + private final ArrayList<RouteConnectionRecord> mConnections + = new ArrayList<RouteConnectionRecord>(); + // The sessions that have a route from this provider selected + private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); private final Handler mHandler = new Handler(); private Intent mBindIntent; private IRouteProvider mBinder; private boolean mRunning; + private boolean mPaused; private boolean mInterested; private boolean mBound; private int mRetryCount; @@ -83,6 +87,12 @@ public class MediaRouteProviderProxy { mBindIntent.setComponent(mComponentName); } + public void destroy() { + stop(); + mSessions.clear(); + updateBinding(); + } + /** * Send any cleanup messages and unbind from the media route provider */ @@ -236,6 +246,19 @@ public class MediaRouteProviderProxy { return mId; } + public void addSession(MediaSessionRecord session) { + mSessions.add(session); + } + + public void removeSession(MediaSessionRecord session) { + mSessions.remove(session); + updateBinding(); + } + + public int getSessionCount() { + return mSessions.size(); + } + public void dump(PrintWriter pw, String prefix) { pw.println(prefix + mId + " " + this); String indent = prefix + " "; @@ -257,8 +280,10 @@ public class MediaRouteProviderProxy { } } + // We want to bind as long as we're interested in this provider or there are + // sessions connected to it. private boolean shouldBind() { - return mRunning && mInterested; + return (mRunning && mInterested) || (!mSessions.isEmpty()); } private void bind() { diff --git a/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java b/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java index cf1d95a..734eab9 100644 --- a/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java +++ b/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java @@ -90,6 +90,8 @@ public class MediaRouteProviderWatcher { } } + // Stop discovering providers and routes. Providers that still have an + // active session connected to them will not unbind. public void stop() { if (mRunning) { mRunning = false; @@ -97,13 +99,21 @@ public class MediaRouteProviderWatcher { mContext.unregisterReceiver(mScanPackagesReceiver); mHandler.removeCallbacks(mScanPackagesRunnable); - // Stop all providers. + // Stop all inactive providers. for (int i = mProviders.size() - 1; i >= 0; i--) { mProviders.get(i).stop(); } } } + // Clean up the providers forcibly unbinding if necessary + public void destroy() { + for (int i = mProviders.size() - 1; i >= 0; i--) { + mProviders.get(i).destroy(); + mProviders.remove(i); + } + } + public ArrayList<MediaRouteProviderProxy> getProviders() { return mProviders; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 41ab626..9677577 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -113,6 +113,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { // End TransportPerformer fields private boolean mIsActive = false; + private boolean mDestroyed = false; public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, ISessionCallback cb, String tag, MediaSessionService service, Handler handler) { @@ -220,10 +221,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { public void selectRoute(RouteInfo route) { synchronized (mLock) { if (route != mRoute) { - if (mConnection != null) { - mConnection.disconnect(); - mConnection = null; - } + disconnect(Session.DISCONNECT_REASON_ROUTE_CHANGED); } mRoute = route; } @@ -261,14 +259,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { public boolean setRouteConnected(RouteInfo route, RouteOptions request, RouteConnectionRecord connection) { synchronized (mLock) { + if (mDestroyed) { + Log.i(TAG, "setRouteConnected: session has been destroyed"); + connection.disconnect(); + return false; + } if (mRoute == null || !TextUtils.equals(route.getId(), mRoute.getId())) { Log.w(TAG, "setRouteConnected: connected route is stale"); - // TODO figure out disconnection path + connection.disconnect(); return false; } if (request != mRequest) { Log.w(TAG, "setRouteConnected: connection request is stale"); - // TODO figure out disconnection path + connection.disconnect(); return false; } mConnection = connection; @@ -284,7 +287,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { * @return True if the session is active, false otherwise. */ public boolean isActive() { - return mIsActive; + return mIsActive && !mDestroyed; } /** @@ -307,6 +310,30 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return false; } + /** + * @return True if this session is currently connected to a route. + */ + public boolean isConnected() { + return mConnection != null; + } + + public void disconnect(int reason) { + synchronized (mLock) { + if (!mDestroyed) { + disconnectLocked(reason); + } + } + } + + private void disconnectLocked(int reason) { + if (mConnection != null) { + mConnection.setListener(null); + mConnection.disconnect(); + mConnection = null; + pushDisconnected(reason); + } + } + public boolean isTransportControlEnabled() { return hasFlag(Session.FLAG_HANDLES_TRANSPORT_CONTROLS); } @@ -316,6 +343,28 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mService.sessionDied(this); } + /** + * Finish cleaning up this session, including disconnecting if connected and + * removing the death observer from the callback binder. + */ + public void onDestroy() { + synchronized (mLock) { + if (mDestroyed) { + return; + } + if (isConnected()) { + disconnectLocked(Session.DISCONNECT_REASON_SESSION_DESTROYED); + } + mRoute = null; + mRequest = null; + mDestroyed = true; + } + } + + public ISessionCallback getCallback() { + return mSessionCb.mCb; + } + public void dump(PrintWriter pw, String prefix) { pw.println(prefix + mTag + " " + this); @@ -323,7 +372,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid + ", userId=" + mUserId); pw.println(indent + "info=" + mSessionInfo.toString()); - pw.println(indent + "published=" + mIsActive); + pw.println(indent + "active=" + mIsActive); pw.println(indent + "flags=" + mFlags); pw.println(indent + "rating type=" + mRatingType); pw.println(indent + "controllers: " + mControllerCallbacks.size()); @@ -356,12 +405,17 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { return "size=" + fields + ", title=" + title; } - private void onDestroy() { - mService.destroySession(this); + private void pushDisconnected(int reason) { + synchronized (mLock) { + mSessionCb.sendRouteDisconnected(reason); + } } private void pushPlaybackStateUpdate() { synchronized (mLock) { + if (mDestroyed) { + return; + } for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { ISessionControllerCallback cb = mControllerCallbacks.get(i); try { @@ -376,6 +430,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private void pushMetadataUpdate() { synchronized (mLock) { + if (mDestroyed) { + return; + } for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { ISessionControllerCallback cb = mControllerCallbacks.get(i); try { @@ -390,6 +447,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private void pushRouteUpdate() { synchronized (mLock) { + if (mDestroyed) { + return; + } for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { ISessionControllerCallback cb = mControllerCallbacks.get(i); try { @@ -404,6 +464,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private void pushEvent(String event, Bundle data) { synchronized (mLock) { + if (mDestroyed) { + return; + } for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) { ISessionControllerCallback cb = mControllerCallbacks.get(i); try { @@ -417,6 +480,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private void pushRouteCommand(RouteCommand command, ResultReceiver cb) { synchronized (mLock) { + if (mDestroyed) { + return; + } if (mRoute == null || !TextUtils.equals(command.getRouteInfo(), mRoute.getId())) { if (cb != null) { cb.send(RouteInterface.RESULT_ROUTE_IS_STALE, null); @@ -470,14 +536,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public void disconnect() { - // TODO + MediaSessionRecord.this.disconnect(Session.DISCONNECT_REASON_PROVIDER_DISCONNECTED); } }; private final class SessionStub extends ISession.Stub { @Override public void destroy() { - onDestroy(); + mService.destroySession(MediaSessionRecord.this); } @Override @@ -554,6 +620,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override + public void disconnectFromRoute(RouteInfo route) { + if (route != null && mRoute != null + && TextUtils.equals(route.getId(), mRoute.getId())) { + disconnect(Session.DISCONNECT_REASON_SESSION_DISCONNECTED); + } + } + + @Override public void setRouteOptions(List<RouteOptions> options) throws RemoteException { mRequests.clear(); for (int i = options.size() - 1; i >= 0; i--) { @@ -621,6 +695,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } + public void sendRouteDisconnected(int reason) { + try { + mCb.onRouteDisconnected(mRoute, reason); + } catch (RemoteException e) { + Slog.e(TAG, "Remote failure in sendRouteDisconnected"); + } + } + public void play() { try { mCb.onPlay(); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 008f9be..78f3b5f 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -24,20 +24,19 @@ import android.content.pm.PackageManager; import android.media.routeprovider.RouteRequest; import android.media.session.ISession; import android.media.session.ISessionCallback; -import android.media.session.ISessionController; import android.media.session.ISessionManager; -import android.media.session.PlaybackState; import android.media.session.RouteInfo; import android.media.session.RouteOptions; +import android.media.session.Session; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import android.util.SparseArray; import com.android.server.SystemService; import com.android.server.Watchdog; @@ -56,16 +55,18 @@ public class MediaSessionService extends SystemService implements Monitor { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final SessionManagerImpl mSessionManagerImpl; - private final MediaRouteProviderWatcher mRouteProviderWatcher; + // private final MediaRouteProviderWatcher mRouteProviderWatcher; private final MediaSessionStack mPriorityStack; - private final ArrayList<MediaSessionRecord> mRecords = new ArrayList<MediaSessionRecord>(); - private final ArrayList<MediaRouteProviderProxy> mProviders - = new ArrayList<MediaRouteProviderProxy>(); + private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>(); + private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>(); + // private final ArrayList<MediaRouteProviderProxy> mProviders + // = new ArrayList<MediaRouteProviderProxy>(); private final Object mLock = new Object(); private final Handler mHandler = new Handler(); private MediaSessionRecord mPrioritySession; + private int mCurrentUserId = -1; // Used to keep track of the current request to show routes for a specific // session so we drop late callbacks properly. @@ -77,16 +78,14 @@ public class MediaSessionService extends SystemService implements Monitor { public MediaSessionService(Context context) { super(context); mSessionManagerImpl = new SessionManagerImpl(); - mRouteProviderWatcher = new MediaRouteProviderWatcher(context, mProviderWatcherCallback, - mHandler, context.getUserId()); mPriorityStack = new MediaSessionStack(); } @Override public void onStart() { publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); - mRouteProviderWatcher.start(); Watchdog.getInstance().addMonitor(this); + updateUser(); } /** @@ -98,16 +97,28 @@ public class MediaSessionService extends SystemService implements Monitor { public void showRoutePickerForSession(MediaSessionRecord record) { // TODO for now just toggle the route to test (we will only have one // match for now) - if (record.getRoute() != null) { - // For now send null to mean the local route - record.selectRoute(null); - return; - } - mShowRoutesRequestId++; - ArrayList<MediaRouteProviderProxy> providers = mRouteProviderWatcher.getProviders(); - for (int i = providers.size() - 1; i >= 0; i--) { - MediaRouteProviderProxy provider = providers.get(i); - provider.getRoutes(record, mShowRoutesRequestId); + synchronized (mLock) { + if (!mAllSessions.contains(record)) { + Log.d(TAG, "Unknown session tried to show route picker. Ignoring."); + return; + } + RouteInfo current = record.getRoute(); + UserRecord user = mUserRecords.get(record.getUserId()); + if (current != null) { + // For now send null to mean the local route + MediaRouteProviderProxy proxy = user.getProviderLocked(current.getProvider()); + if (proxy != null) { + proxy.removeSession(record); + } + record.selectRoute(null); + return; + } + ArrayList<MediaRouteProviderProxy> providers = user.getProvidersLocked(); + mShowRoutesRequestId++; + for (int i = providers.size() - 1; i >= 0; i--) { + MediaRouteProviderProxy provider = providers.get(i); + provider.getRoutes(record, mShowRoutesRequestId); + } } } @@ -121,19 +132,31 @@ public class MediaSessionService extends SystemService implements Monitor { public void connectToRoute(MediaSessionRecord session, RouteInfo route, RouteOptions options) { synchronized (mLock) { - MediaRouteProviderProxy proxy = getProviderLocked(route.getProvider()); + if (!mAllSessions.contains(session)) { + Log.d(TAG, "Unknown session attempting to connect to route. Ignoring"); + return; + } + UserRecord user = mUserRecords.get(session.getUserId()); + if (user == null) { + Log.wtf(TAG, "connectToRoute: User " + session.getUserId() + " does not exist."); + return; + } + MediaRouteProviderProxy proxy = user.getProviderLocked(route.getProvider()); if (proxy == null) { Log.w(TAG, "Provider for route " + route.getName() + " does not exist."); return; } RouteRequest request = new RouteRequest(session.getSessionInfo(), options, true); - // TODO make connect an async call to a ThreadPoolExecutor proxy.connectToRoute(session, route, request); } } public void updateSession(MediaSessionRecord record) { synchronized (mLock) { + if (!mAllSessions.contains(record)) { + Log.d(TAG, "Unknown session updated. Ignoring."); + return; + } mPriorityStack.onSessionStateChange(record); if (record.isSystemPriority()) { if (record.isActive()) { @@ -152,17 +175,48 @@ public class MediaSessionService extends SystemService implements Monitor { public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { synchronized (mLock) { + if (!mAllSessions.contains(record)) { + Log.d(TAG, "Unknown session changed playback state. Ignoring."); + return; + } mPriorityStack.onPlaystateChange(record, oldState, newState); } } @Override + public void onStartUser(int userHandle) { + updateUser(); + } + + @Override + public void onSwitchUser(int userHandle) { + updateUser(); + } + + @Override + public void onStopUser(int userHandle) { + synchronized (mLock) { + UserRecord user = mUserRecords.get(userHandle); + if (user != null) { + destroyUserLocked(user); + } + } + } + + @Override public void monitor() { synchronized (mLock) { // Check for deadlock } } + protected void enforcePhoneStatePermission(int pid, int uid) { + if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); + } + } + void sessionDied(MediaSessionRecord session) { synchronized (mLock) { destroySessionLocked(session); @@ -175,12 +229,63 @@ public class MediaSessionService extends SystemService implements Monitor { } } + private void updateUser() { + synchronized (mLock) { + int userId = ActivityManager.getCurrentUser(); + if (mCurrentUserId != userId) { + final int oldUserId = mCurrentUserId; + mCurrentUserId = userId; // do this first + + UserRecord oldUser = mUserRecords.get(oldUserId); + if (oldUser != null) { + oldUser.stopLocked(); + } + + UserRecord newUser = getOrCreateUser(userId); + newUser.startLocked(); + } + } + } + + /** + * Stop the user and unbind from everything. + * + * @param user The user to dispose of + */ + private void destroyUserLocked(UserRecord user) { + user.stopLocked(); + user.destroyLocked(); + mUserRecords.remove(user.mUserId); + } + + /* + * When a session is removed several things need to happen. + * 1. We need to remove it from the relevant user. + * 2. We need to remove it from the priority stack. + * 3. We need to remove it from all sessions. + * 4. If this is the system priority session we need to clear it. + * 5. We need to unlink to death from the cb binder + * 6. We need to tell the session to do any final cleanup (onDestroy) + */ private void destroySessionLocked(MediaSessionRecord session) { - mRecords.remove(session); + int userId = session.getUserId(); + UserRecord user = mUserRecords.get(userId); + if (user != null) { + user.removeSessionLocked(session); + } + mPriorityStack.removeSession(session); + mAllSessions.remove(session); if (session == mPrioritySession) { mPrioritySession = null; } + + try { + session.getCallback().asBinder().unlinkToDeath(session, 0); + } catch (Exception e) { + // ignore exceptions while destroying a session. + } + session.onDestroy(); } private void enforcePackageName(String packageName, int uid) { @@ -197,13 +302,6 @@ public class MediaSessionService extends SystemService implements Monitor { throw new IllegalArgumentException("packageName is not owned by the calling process"); } - protected void enforcePhoneStatePermission(int pid, int uid) { - if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission."); - } - } - /** * Checks a caller's authorization to register an IRemoteControlDisplay. * Authorization is granted if one of the following is true: @@ -271,14 +369,22 @@ public class MediaSessionService extends SystemService implements Monitor { } private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, - String callerPackageName, ISessionCallback cb, String tag) { + String callerPackageName, ISessionCallback cb, String tag) throws RemoteException { synchronized (mLock) { return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); } } + /* + * When a session is created the following things need to happen. + * 1. It's callback binder needs a link to death + * 2. It needs to be added to all sessions. + * 3. It needs to be added to the priority stack. + * 4. It needs to be added to the relevant user record. + */ private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, String callerPackageName, ISessionCallback cb, String tag) { + final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, callerPackageName, cb, tag, this, mHandler); try { @@ -286,17 +392,31 @@ public class MediaSessionService extends SystemService implements Monitor { } catch (RemoteException e) { throw new RuntimeException("Media Session owner died prematurely.", e); } - mRecords.add(session); + + mAllSessions.add(session); mPriorityStack.addSession(session); + + UserRecord user = getOrCreateUser(userId); + user.addSessionLocked(session); + if (DEBUG) { Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag); } return session; } + private UserRecord getOrCreateUser(int userId) { + UserRecord user = mUserRecords.get(userId); + if (user == null) { + user = new UserRecord(getContext(), userId); + mUserRecords.put(userId, user); + } + return user; + } + private int findIndexOfSessionForIdLocked(String sessionId) { - for (int i = mRecords.size() - 1; i >= 0; i--) { - MediaSessionRecord session = mRecords.get(i); + for (int i = mAllSessions.size() - 1; i >= 0; i--) { + MediaSessionRecord session = mAllSessions.get(i); if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) { return i; } @@ -304,42 +424,11 @@ public class MediaSessionService extends SystemService implements Monitor { return -1; } - private MediaRouteProviderProxy getProviderLocked(String providerId) { - for (int i = mProviders.size() - 1; i >= 0; i--) { - MediaRouteProviderProxy provider = mProviders.get(i); - if (TextUtils.equals(providerId, provider.getId())) { - return provider; - } - } - return null; - } - private boolean isSessionDiscoverable(MediaSessionRecord record) { - // TODO probably want to check more than if it's published. + // TODO probably want to check more than if it's active. return record.isActive(); } - private MediaRouteProviderWatcher.Callback mProviderWatcherCallback - = new MediaRouteProviderWatcher.Callback() { - @Override - public void removeProvider(MediaRouteProviderProxy provider) { - synchronized (mLock) { - mProviders.remove(provider); - provider.setRoutesListener(null); - provider.setInterested(false); - } - } - - @Override - public void addProvider(MediaRouteProviderProxy provider) { - synchronized (mLock) { - mProviders.add(provider); - provider.setRoutesListener(mRoutesCallback); - provider.setInterested(true); - } - } - }; - private MediaRouteProviderProxy.RoutesListener mRoutesCallback = new MediaRouteProviderProxy.RoutesListener() { @Override @@ -350,8 +439,12 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int index = findIndexOfSessionForIdLocked(sessionId); if (index != -1 && routes != null && routes.size() > 0) { - MediaSessionRecord record = mRecords.get(index); - record.selectRoute(routes.get(0)); + MediaSessionRecord record = mAllSessions.get(index); + RouteInfo route = routes.get(0); + record.selectRoute(route); + UserRecord user = mUserRecords.get(record.getUserId()); + MediaRouteProviderProxy provider = user.getProviderLocked(route.getProvider()); + provider.addSession(record); } } } @@ -362,13 +455,135 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int index = findIndexOfSessionForIdLocked(sessionId); if (index != -1) { - MediaSessionRecord session = mRecords.get(index); + MediaSessionRecord session = mAllSessions.get(index); session.setRouteConnected(route, options.getConnectionOptions(), connection); } } } }; + /** + * Information about a particular user. The contents of this object is + * guarded by mLock. + */ + final class UserRecord { + private final int mUserId; + private final MediaRouteProviderWatcher mRouteProviderWatcher; + private final ArrayList<MediaRouteProviderProxy> mProviders + = new ArrayList<MediaRouteProviderProxy>(); + private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); + + public UserRecord(Context context, int userId) { + mUserId = userId; + mRouteProviderWatcher = new MediaRouteProviderWatcher(context, + mProviderWatcherCallback, mHandler, userId); + } + + public void startLocked() { + mRouteProviderWatcher.start(); + } + + public void stopLocked() { + mRouteProviderWatcher.stop(); + updateInterestLocked(); + } + + public void destroyLocked() { + for (int i = mSessions.size() - 1; i >= 0; i--) { + MediaSessionRecord session = mSessions.get(i); + MediaSessionService.this.destroySessionLocked(session); + if (session.isConnected()) { + session.disconnect(Session.DISCONNECT_REASON_USER_STOPPING); + } + } + } + + public ArrayList<MediaRouteProviderProxy> getProvidersLocked() { + return mProviders; + } + + public ArrayList<MediaSessionRecord> getSessionsLocked() { + return mSessions; + } + + public void addSessionLocked(MediaSessionRecord session) { + mSessions.add(session); + updateInterestLocked(); + } + + public void removeSessionLocked(MediaSessionRecord session) { + mSessions.remove(session); + RouteInfo route = session.getRoute(); + if (route != null) { + MediaRouteProviderProxy provider = getProviderLocked(route.getProvider()); + if (provider != null) { + provider.removeSession(session); + } + } + updateInterestLocked(); + } + + public void dumpLocked(PrintWriter pw, String prefix) { + pw.println(prefix + "Record for user " + mUserId); + String indent = prefix + " "; + int size = mProviders.size(); + pw.println(indent + size + " Providers:"); + for (int i = 0; i < size; i++) { + mProviders.get(i).dump(pw, indent); + } + pw.println(); + size = mSessions.size(); + pw.println(indent + size + " Sessions:"); + for (int i = 0; i < size; i++) { + // Just print the session info, the full session dump will + // already be in the list of all sessions. + pw.println(indent + mSessions.get(i).getSessionInfo()); + } + } + + public void updateInterestLocked() { + // TODO go through the sessions and build up the set of interfaces + // we're interested in. Update the provider watcher. + // For now, just express interest in all providers for the current + // user + boolean interested = mUserId == mCurrentUserId; + for (int i = mProviders.size() - 1; i >= 0; i--) { + mProviders.get(i).setInterested(interested); + } + } + + private MediaRouteProviderProxy getProviderLocked(String providerId) { + for (int i = mProviders.size() - 1; i >= 0; i--) { + MediaRouteProviderProxy provider = mProviders.get(i); + if (TextUtils.equals(providerId, provider.getId())) { + return provider; + } + } + return null; + } + + private MediaRouteProviderWatcher.Callback mProviderWatcherCallback + = new MediaRouteProviderWatcher.Callback() { + @Override + public void removeProvider(MediaRouteProviderProxy provider) { + synchronized (mLock) { + mProviders.remove(provider); + provider.setRoutesListener(null); + provider.setInterested(false); + } + } + + @Override + public void addProvider(MediaRouteProviderProxy provider) { + synchronized (mLock) { + mProviders.add(provider); + provider.setRoutesListener(mRoutesCallback); + provider.setInterested(true); + } + } + }; + } + class SessionManagerImpl extends ISessionManager.Stub { // TODO add createSessionAsUser, pass user-id to // ActivityManagerNative.handleIncomingUser and stash result for use @@ -447,19 +662,19 @@ public class MediaSessionService extends SystemService implements Monitor { if (mPrioritySession != null) { mPrioritySession.dump(pw, ""); } - int count = mRecords.size(); + int count = mAllSessions.size(); pw.println(count + " Sessions:"); for (int i = 0; i < count; i++) { - mRecords.get(i).dump(pw, ""); + mAllSessions.get(i).dump(pw, ""); pw.println(); } mPriorityStack.dump(pw, ""); - pw.println("Providers:"); - count = mProviders.size(); + pw.println("User Records:"); + count = mUserRecords.size(); for (int i = 0; i < count; i++) { - MediaRouteProviderProxy provider = mProviders.get(i); - provider.dump(pw, ""); + UserRecord user = mUserRecords.get(i); + user.dumpLocked(pw, ""); } } } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 1e1818d..f89b14a 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -19,7 +19,6 @@ package com.android.server.media; import android.media.session.PlaybackState; import android.media.session.Session; import android.os.UserHandle; -import android.text.TextUtils; import java.io.PrintWriter; import java.util.ArrayList; @@ -49,6 +48,8 @@ public class MediaSessionStack { private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); + private MediaSessionRecord mGlobalPrioritySession; + private MediaSessionRecord mCachedButtonReceiver; private MediaSessionRecord mCachedDefault; private ArrayList<MediaSessionRecord> mCachedActiveList; @@ -61,6 +62,9 @@ public class MediaSessionStack { */ public void addSession(MediaSessionRecord record) { mSessions.add(record); + if ((record.getFlags() & Session.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { + mGlobalPrioritySession = record; + } clearCache(); } @@ -71,6 +75,9 @@ public class MediaSessionStack { */ public void removeSession(MediaSessionRecord record) { mSessions.remove(record); + if (record == mGlobalPrioritySession) { + mGlobalPrioritySession = null; + } clearCache(); } @@ -156,6 +163,9 @@ public class MediaSessionStack { * @return The default media button session or null. */ public MediaSessionRecord getDefaultMediaButtonSession(int userId) { + if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { + return mGlobalPrioritySession; + } if (mCachedButtonReceiver != null) { return mCachedButtonReceiver; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2f1d291..1734a33 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -727,6 +727,22 @@ public class NotificationManagerService extends SystemService { EventLogTags.writeNotificationVisibilityChanged( TextUtils.join(";", newlyVisibleKeys), TextUtils.join(";", noLongerVisibleKeys)); + synchronized (mNotificationList) { + for (String key : newlyVisibleKeys) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r == null) continue; + r.stats.onVisibilityChanged(true); + } + // Note that we might receive this event after notifications + // have already left the system, e.g. after dismissing from the + // shade. Hence not finding notifications in + // mNotificationsByKey is not an exceptional condition. + for (String key : noLongerVisibleKeys) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r == null) continue; + r.stats.onVisibilityChanged(false); + } + } } }; diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index a60e95b..d81a25e 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -80,6 +80,7 @@ public class NotificationUsageStats { * Called when the originating app removed the notification programmatically. */ public synchronized void registerRemovedByApp(NotificationRecord notification) { + notification.stats.onRemoved(); for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.numRemovedByApp++; stats.collect(notification.stats); @@ -116,9 +117,7 @@ public class NotificationUsageStats { * <p>Called after {@link #registerClickedByUser(NotificationRecord)}.</p> */ public synchronized void registerCancelDueToClick(NotificationRecord notification) { - // No explicit stats for this (the click has already been registered in - // registerClickedByUser), just make sure the single notification stats - // are folded up into aggregated stats. + notification.stats.onCancel(); for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.collect(notification.stats); } @@ -130,7 +129,7 @@ public class NotificationUsageStats { * <p>Called for notifications of apps being uninstalled, for example.</p> */ public synchronized void registerCancelUnknown(NotificationRecord notification) { - // Fold up individual stats. + notification.stats.onCancel(); for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.collect(notification.stats); } @@ -185,6 +184,9 @@ public class NotificationUsageStats { public final Aggregate posttimeMs = new Aggregate(); public final Aggregate posttimeToDismissMs = new Aggregate(); public final Aggregate posttimeToFirstClickMs = new Aggregate(); + public final Aggregate airtimeCount = new Aggregate(); + public final Aggregate airtimeMs = new Aggregate(); + public final Aggregate posttimeToFirstAirtimeMs = new Aggregate(); public AggregatedStats(String key) { this.key = key; @@ -199,6 +201,14 @@ public class NotificationUsageStats { if (singleNotificationStats.posttimeToFirstClickMs >= 0) { posttimeToFirstClickMs.addSample(singleNotificationStats.posttimeToFirstClickMs); } + airtimeCount.addSample(singleNotificationStats.airtimeCount); + if (singleNotificationStats.airtimeMs >= 0) { + airtimeMs.addSample(singleNotificationStats.airtimeMs); + } + if (singleNotificationStats.posttimeToFirstAirtimeMs >= 0) { + posttimeToFirstAirtimeMs.addSample( + singleNotificationStats.posttimeToFirstAirtimeMs); + } } public void dump(PrintWriter pw, String indent) { @@ -221,6 +231,9 @@ public class NotificationUsageStats { indent + " posttimeMs=" + posttimeMs + ",\n" + indent + " posttimeToDismissMs=" + posttimeToDismissMs + ",\n" + indent + " posttimeToFirstClickMs=" + posttimeToFirstClickMs + ",\n" + + indent + " airtimeCount=" + airtimeCount + ",\n" + + indent + " airtimeMs=" + airtimeMs + ",\n" + + indent + " posttimeToFirstAirtimeMs=" + posttimeToFirstAirtimeMs + ",\n" + indent + "}"; } } @@ -235,6 +248,33 @@ public class NotificationUsageStats { public long posttimeToFirstClickMs = -1; /** Elpased time since the notification was posted until it was dismissed by the user. */ public long posttimeToDismissMs = -1; + /** Number of times the notification has been made visible. */ + public long airtimeCount = 0; + /** Time in ms between the notification was posted and first shown; -1 if never shown. */ + public long posttimeToFirstAirtimeMs = -1; + /** + * If currently visible, SystemClock.elapsedRealtime() when the notification was made + * visible; -1 otherwise. + */ + public long currentAirtimeStartElapsedMs = -1; + /** Accumulated visible time. */ + public long airtimeMs = 0; + + public long getCurrentPosttimeMs() { + if (posttimeElapsedMs < 0) { + return 0; + } + return SystemClock.elapsedRealtime() - posttimeElapsedMs; + } + + public long getCurrentAirtimeMs() { + long result = airtimeMs; + // Add incomplete airtime if currently shown. + if (currentAirtimeStartElapsedMs >= 0) { + result+= (SystemClock.elapsedRealtime() - currentAirtimeStartElapsedMs); + } + return result; + } /** * Called when the user clicked the notification. @@ -252,6 +292,38 @@ public class NotificationUsageStats { if (posttimeToDismissMs < 0) { posttimeToDismissMs = SystemClock.elapsedRealtime() - posttimeElapsedMs; } + finish(); + } + + public void onCancel() { + finish(); + } + + public void onRemoved() { + finish(); + } + + public void onVisibilityChanged(boolean visible) { + long elapsedNowMs = SystemClock.elapsedRealtime(); + if (visible) { + if (currentAirtimeStartElapsedMs < 0) { + airtimeCount++; + currentAirtimeStartElapsedMs = elapsedNowMs; + } + if (posttimeToFirstAirtimeMs < 0) { + posttimeToFirstAirtimeMs = elapsedNowMs - posttimeElapsedMs; + } + } else { + if (currentAirtimeStartElapsedMs >= 0) { + airtimeMs += (elapsedNowMs - currentAirtimeStartElapsedMs); + currentAirtimeStartElapsedMs = -1; + } + } + } + + /** The notification is leaving the system. Finalize. */ + public void finish() { + onVisibilityChanged(false); } @Override @@ -260,6 +332,9 @@ public class NotificationUsageStats { "posttimeElapsedMs=" + posttimeElapsedMs + ", posttimeToFirstClickMs=" + posttimeToFirstClickMs + ", posttimeToDismissMs=" + posttimeToDismissMs + + ", airtimeCount=" + airtimeCount + + ", airtimeMs=" + airtimeMs + + ", currentAirtimeStartElapsedMs=" + currentAirtimeStartElapsedMs + '}'; } } @@ -305,7 +380,7 @@ public class NotificationUsageStats { private static final int MSG_DISMISS = 4; private static final String DB_NAME = "notification_log.db"; - private static final int DB_VERSION = 1; + private static final int DB_VERSION = 2; /** Age in ms after which events are pruned from the DB. */ private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week @@ -329,6 +404,8 @@ public class NotificationUsageStats { private static final String COL_PRIORITY = "priority"; private static final String COL_CATEGORY = "category"; private static final String COL_ACTION_COUNT = "action_count"; + private static final String COL_POSTTIME_MS = "posttime_ms"; + private static final String COL_AIRTIME_MS = "airtime_ms"; private static final int EVENT_TYPE_POST = 1; private static final int EVENT_TYPE_CLICK = 2; @@ -354,16 +431,16 @@ public class NotificationUsageStats { long nowMs = System.currentTimeMillis(); switch (msg.what) { case MSG_POST: - writeEvent(r.sbn.getPostTime(), EVENT_TYPE_POST, r, true); + writeEvent(r.sbn.getPostTime(), EVENT_TYPE_POST, r); break; case MSG_CLICK: - writeEvent(nowMs, EVENT_TYPE_CLICK, r, false); + writeEvent(nowMs, EVENT_TYPE_CLICK, r); break; case MSG_REMOVE: - writeEvent(nowMs, EVENT_TYPE_REMOVE, r, false); + writeEvent(nowMs, EVENT_TYPE_REMOVE, r); break; case MSG_DISMISS: - writeEvent(nowMs, EVENT_TYPE_DISMISS, r, false); + writeEvent(nowMs, EVENT_TYPE_DISMISS, r); break; default: Log.wtf(TAG, "Unknown message type: " + msg.what); @@ -388,14 +465,22 @@ public class NotificationUsageStats { COL_FLAGS + " INT," + COL_PRIORITY + " INT," + COL_CATEGORY + " TEXT," + - COL_ACTION_COUNT + " INT" + + COL_ACTION_COUNT + " INT," + + COL_POSTTIME_MS + " INT," + + COL_AIRTIME_MS + " INT" + ")"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG); - onCreate(db); + switch (oldVersion) { + case 1: + // Add COL_POSTTIME_MS, COL_AIRTIME_MS columns, + db.execSQL("ALTER TABLE " + TAB_LOG + " ADD COLUMN " + + COL_POSTTIME_MS + " INT"); + db.execSQL("ALTER TABLE " + TAB_LOG + " ADD COLUMN " + + COL_AIRTIME_MS + " INT"); + } } }; } @@ -445,15 +530,16 @@ public class NotificationUsageStats { } } - private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r, - boolean populateNotificationDetails) { + private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r) { ContentValues cv = new ContentValues(); cv.put(COL_EVENT_USER_ID, r.sbn.getUser().getIdentifier()); cv.put(COL_EVENT_TIME, eventTimeMs); cv.put(COL_EVENT_TYPE, eventType); putNotificationIdentifiers(r, cv); - if (populateNotificationDetails) { + if (eventType == EVENT_TYPE_POST) { putNotificationDetails(r, cv); + } else { + putPosttimeAirtime(r, cv); } SQLiteDatabase db = mHelper.getWritableDatabase(); if (db.insert(TAB_LOG, null, cv) < 0) { @@ -497,6 +583,11 @@ public class NotificationUsageStats { r.getNotification().actions.length : 0); } + private static void putPosttimeAirtime(NotificationRecord r, ContentValues outCv) { + outCv.put(COL_POSTTIME_MS, r.stats.getCurrentPosttimeMs()); + outCv.put(COL_AIRTIME_MS, r.stats.getCurrentAirtimeMs()); + } + public void dump(PrintWriter pw, String indent) { printPostFrequencies(pw, indent); } diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java index 8cd2f9b2..b5c2730 100644 --- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -45,13 +45,13 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private static final boolean ENABLE_PEOPLE_VALIDATOR = true; private static final String SETTING_ENABLE_PEOPLE_VALIDATOR = "validate_notification_people_enabled"; - private static final String[] LOOKUP_PROJECTION = { Contacts._ID }; + private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.STARRED }; private static final int MAX_PEOPLE = 10; private static final int PEOPLE_CACHE_SIZE = 200; private static final float NONE = 0f; private static final float VALID_CONTACT = 0.5f; - // TODO private static final float STARRED_CONTACT = 1f; + private static final float STARRED_CONTACT = 1f; protected boolean mEnabled; private Context mContext; @@ -104,23 +104,29 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { public void work() { if (INFO) Slog.i(TAG, "Executing: validation for: " + mRecord.sbn.getKey()); float affinity = NONE; - LookupResult lookupResult = null; for (final String handle: pendingLookups) { + LookupResult lookupResult = null; final Uri uri = Uri.parse(handle); if ("tel".equals(uri.getScheme())) { if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle); - lookupResult = resolvePhoneContact(handle, uri.getSchemeSpecificPart()); + lookupResult = resolvePhoneContact(uri.getSchemeSpecificPart()); + } else if ("mailto".equals(uri.getScheme())) { + if (DEBUG) Slog.d(TAG, "checking mailto URI: " + handle); + lookupResult = resolveEmailContact(uri.getSchemeSpecificPart()); } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle); - lookupResult = resolveContactsUri(handle, uri); + lookupResult = searchContacts(uri); } else { + lookupResult = new LookupResult(); // invalid person for the cache Slog.w(TAG, "unsupported URI " + handle); } + if (lookupResult != null) { + synchronized (mPeopleCache) { + mPeopleCache.put(handle, lookupResult); + } + affinity = Math.max(affinity, lookupResult.getAffinity()); + } } - if (lookupResult != null) { - affinity = Math.max(affinity, lookupResult.getAffinity()); - } - float affinityBound = mRecord.getContactAffinity(); affinity = Math.max(affinity, affinityBound); mRecord.setContactAffinity(affinity); @@ -183,47 +189,27 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { return null; } - private LookupResult resolvePhoneContact(final String handle, final String number) { - LookupResult lookupResult = null; - Cursor c = null; - try { - Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, - Uri.encode(number)); - c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null); - if (c != null && c.getCount() > 0) { - c.moveToFirst(); - final int idIdx = c.getColumnIndex(Contacts._ID); - final int id = c.getInt(idIdx); - if (DEBUG) Slog.d(TAG, "is valid: " + id); - lookupResult = new LookupResult(id); - } - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - if (lookupResult == null) { - lookupResult = new LookupResult(LookupResult.INVALID_ID); - } - synchronized (mPeopleCache) { - mPeopleCache.put(handle, lookupResult); - } - return lookupResult; + private LookupResult resolvePhoneContact(final String number) { + Uri phoneUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, + Uri.encode(number)); + return searchContacts(phoneUri); + } + + private LookupResult resolveEmailContact(final String email) { + Uri numberUri = Uri.withAppendedPath( + ContactsContract.CommonDataKinds.Email.CONTENT_LOOKUP_URI, + Uri.encode(email)); + return searchContacts(numberUri); } - private LookupResult resolveContactsUri(String handle, final Uri personUri) { - LookupResult lookupResult = null; + private LookupResult searchContacts(Uri lookupUri) { + LookupResult lookupResult = new LookupResult(); Cursor c = null; try { - c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null); + c = mContext.getContentResolver().query(lookupUri, LOOKUP_PROJECTION, null, null, null); if (c != null && c.getCount() > 0) { c.moveToFirst(); - final int idIdx = c.getColumnIndex(Contacts._ID); - final int id = c.getInt(idIdx); - if (DEBUG) Slog.d(TAG, "is valid: " + id); - lookupResult = new LookupResult(id); + lookupResult.readContact(c); } } catch(Throwable t) { Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); @@ -232,12 +218,6 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { c.close(); } } - if (lookupResult == null) { - lookupResult = new LookupResult(LookupResult.INVALID_ID); - } - synchronized (mPeopleCache) { - mPeopleCache.put(handle, lookupResult); - } return lookupResult; } @@ -267,12 +247,31 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { private final long mExpireMillis; private int mId; + private boolean mStarred; - public LookupResult(int id) { - mId = id; + public LookupResult() { + mId = INVALID_ID; + mStarred = false; mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS; } + public void readContact(Cursor cursor) { + final int idIdx = cursor.getColumnIndex(Contacts._ID); + if (idIdx >= 0) { + mId = cursor.getInt(idIdx); + if (DEBUG) Slog.d(TAG, "contact _ID is: " + mId); + } else { + if (DEBUG) Slog.d(TAG, "invalid cursor: no _ID"); + } + final int starIdx = cursor.getColumnIndex(Contacts.STARRED); + if (starIdx >= 0) { + mStarred = cursor.getInt(starIdx) != 0; + if (DEBUG) Slog.d(TAG, "contact STARRED is: " + mStarred); + } else { + if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED"); + } + } + public boolean isExpired() { return mExpireMillis < System.currentTimeMillis(); } @@ -284,11 +283,18 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { public float getAffinity() { if (isInvalid()) { return NONE; + } else if (mStarred) { + return STARRED_CONTACT; } else { - return VALID_CONTACT; // TODO: finer grained result: stars + return VALID_CONTACT; } } + public LookupResult setStarred(boolean starred) { + mStarred = starred; + return this; + } + public LookupResult setId(int id) { mId = id; return this; diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java new file mode 100644 index 0000000..5fdfce4 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -0,0 +1,210 @@ +/* + * 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.pm; + +import static android.content.pm.PackageManager.INSTALL_ALL_USERS; +import static android.content.pm.PackageManager.INSTALL_FROM_ADB; +import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.IPackageDeleteObserver; +import android.content.pm.IPackageInstaller; +import android.content.pm.IPackageInstallerSession; +import android.content.pm.PackageInstallerParams; +import android.os.Binder; +import android.os.FileUtils; +import android.os.HandlerThread; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; +import com.android.server.IoThread; +import com.google.android.collect.Sets; + +import java.io.File; + +public class PackageInstallerService extends IPackageInstaller.Stub { + private static final String TAG = "PackageInstaller"; + + // TODO: destroy sessions with old timestamps + // TODO: remove outstanding sessions when installer package goes away + + private final Context mContext; + private final PackageManagerService mPm; + private final AppOpsManager mAppOps; + + private final File mStagingDir; + + private final HandlerThread mInstallThread = new HandlerThread(TAG); + private final Callback mCallback = new Callback(); + + @GuardedBy("mSessions") + private int mNextSessionId; + @GuardedBy("mSessions") + private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>(); + + public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) { + mContext = context; + mPm = pm; + mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + + mStagingDir = stagingDir; + mStagingDir.mkdirs(); + + synchronized (mSessions) { + readSessionsLocked(); + + // Clean up orphaned staging directories + final ArraySet<String> dirs = Sets.newArraySet(mStagingDir.list()); + for (int i = 0; i < mSessions.size(); i++) { + dirs.remove(Integer.toString(mSessions.keyAt(i))); + } + for (String dirName : dirs) { + Slog.w(TAG, "Deleting orphan session " + dirName); + final File dir = new File(mStagingDir, dirName); + FileUtils.deleteContents(dir); + dir.delete(); + } + } + } + + private void readSessionsLocked() { + // TODO: implement persisting + mSessions.clear(); + mNextSessionId = 1; + } + + private void writeSessionsLocked() { + // TODO: implement persisting + } + + private void writeSessionsAsync() { + IoThread.getHandler().post(new Runnable() { + @Override + public void run() { + synchronized (mSessions) { + writeSessionsLocked(); + } + } + }); + } + + @Override + public int createSession(int userId, String installerPackageName, + PackageInstallerParams params) { + final int callingUid = Binder.getCallingUid(); + mPm.enforceCrossUserPermission(callingUid, userId, false, TAG); + mAppOps.checkPackage(callingUid, installerPackageName); + + if (mPm.isUserRestricted(UserHandle.getUserId(callingUid), + UserManager.DISALLOW_INSTALL_APPS)) { + throw new SecurityException("User restriction prevents installing"); + } + + if ((callingUid == Process.SHELL_UID) || (callingUid == 0)) { + params.installFlags |= INSTALL_FROM_ADB; + } else { + params.installFlags &= ~INSTALL_FROM_ADB; + params.installFlags &= ~INSTALL_ALL_USERS; + params.installFlags |= INSTALL_REPLACE_EXISTING; + } + + synchronized (mSessions) { + final int sessionId = allocateSessionIdLocked(); + final long createdMillis = System.currentTimeMillis(); + final File sessionDir = new File(mStagingDir, Integer.toString(sessionId)); + sessionDir.mkdirs(); + + final PackageInstallerSession session = new PackageInstallerSession(mCallback, mPm, + sessionId, userId, installerPackageName, callingUid, params, createdMillis, + sessionDir, mInstallThread.getLooper()); + mSessions.put(sessionId, session); + + writeSessionsAsync(); + return sessionId; + } + } + + @Override + public IPackageInstallerSession openSession(int sessionId) { + synchronized (mSessions) { + final PackageInstallerSession session = mSessions.get(sessionId); + if (session == null) { + throw new IllegalStateException("Missing session " + sessionId); + } + if (Binder.getCallingUid() != session.installerUid) { + throw new SecurityException("Caller has no access to session " + sessionId); + } + return session; + } + } + + private int allocateSessionIdLocked() { + if (mSessions.get(mNextSessionId) != null) { + throw new IllegalStateException("Next session already allocated"); + } + return mNextSessionId++; + } + + @Override + public int[] getSessions(int userId, String installerPackageName) { + final int callingUid = Binder.getCallingUid(); + mPm.enforceCrossUserPermission(callingUid, userId, false, TAG); + mAppOps.checkPackage(callingUid, installerPackageName); + + int[] matching = new int[0]; + synchronized (mSessions) { + for (int i = 0; i < mSessions.size(); i++) { + final int key = mSessions.keyAt(i); + final PackageInstallerSession session = mSessions.valueAt(i); + if (session.userId == userId + && session.installerPackageName.equals(installerPackageName)) { + matching = ArrayUtils.appendInt(matching, key); + } + } + } + return matching; + } + + @Override + public void uninstall(int userId, String basePackageName, IPackageDeleteObserver observer) { + mPm.deletePackageAsUser(basePackageName, observer, userId, 0); + } + + @Override + public void uninstallSplit(int userId, String basePackageName, String overlayName, + IPackageDeleteObserver observer) { + // TODO: flesh out once PM has split support + throw new UnsupportedOperationException(); + } + + class Callback { + public void onProgressChanged(PackageInstallerSession session) { + // TODO: notify listeners + } + + public void onSessionInvalid(PackageInstallerSession session) { + writeSessionsAsync(); + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java new file mode 100644 index 0000000..f90d7ab --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -0,0 +1,539 @@ +/* + * 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.pm; + +import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; +import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; +import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; +import static android.content.pm.PackageManager.INSTALL_SUCCEEDED; + +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageInstallObserver2; +import android.content.pm.IPackageInstallerSession; +import android.content.pm.PackageInstallerParams; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageLite; +import android.content.pm.Signature; +import android.os.Build; +import android.os.Bundle; +import android.os.FileUtils; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.SELinux; +import android.system.ErrnoException; +import android.system.OsConstants; +import android.system.StructStat; +import android.util.ArraySet; +import android.util.Slog; + +import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.Preconditions; + +import libcore.io.IoUtils; +import libcore.io.Libcore; +import libcore.io.Streams; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +public class PackageInstallerSession extends IPackageInstallerSession.Stub { + private static final String TAG = "PackageInstaller"; + + private final PackageInstallerService.Callback mCallback; + private final PackageManagerService mPm; + private final Handler mHandler; + + public final int sessionId; + public final int userId; + public final String installerPackageName; + /** UID not persisted */ + public final int installerUid; + public final PackageInstallerParams params; + public final long createdMillis; + public final File sessionDir; + + private static final int MSG_INSTALL = 0; + + private Handler.Callback mHandlerCallback = new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + synchronized (mLock) { + if (msg.obj != null) { + mRemoteObserver = (IPackageInstallObserver2) msg.obj; + } + + try { + installLocked(); + } catch (InstallFailedException e) { + Slog.e(TAG, "Install failed: " + e); + try { + mRemoteObserver.packageInstalled(mPackageName, null, e.error); + } catch (RemoteException ignored) { + } + } + + return true; + } + } + }; + + private final Object mLock = new Object(); + + private int mProgress; + + private String mPackageName; + private int mVersionCode; + private Signature[] mSignatures; + + private boolean mMutationsAllowed; + private boolean mVerifierConfirmed; + private boolean mPermissionsConfirmed; + private boolean mInvalid; + + private ArrayList<WritePipe> mPipes = new ArrayList<>(); + + private IPackageInstallObserver2 mRemoteObserver; + + public PackageInstallerSession(PackageInstallerService.Callback callback, + PackageManagerService pm, int sessionId, int userId, String installerPackageName, + int installerUid, PackageInstallerParams params, long createdMillis, File sessionDir, + Looper looper) { + mCallback = callback; + mPm = pm; + mHandler = new Handler(looper, mHandlerCallback); + + this.sessionId = sessionId; + this.userId = userId; + this.installerPackageName = installerPackageName; + this.installerUid = installerUid; + this.params = params; + this.createdMillis = createdMillis; + this.sessionDir = sessionDir; + + // Check against any explicitly provided signatures + mSignatures = params.signatures; + + // TODO: splice in flag when restoring persisted session + mMutationsAllowed = true; + + if (pm.checkPermission(android.Manifest.permission.INSTALL_PACKAGES, installerPackageName) + == PackageManager.PERMISSION_GRANTED) { + mPermissionsConfirmed = true; + } + } + + @Override + public void updateProgress(int progress) { + mProgress = progress; + mCallback.onProgressChanged(this); + } + + @Override + public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) { + // TODO: relay over to DCS when installing to ASEC + + // Quick sanity check of state, and allocate a pipe for ourselves. We + // then do heavy disk allocation outside the lock, but this open pipe + // will block any attempted install transitions. + final WritePipe pipe; + synchronized (mLock) { + if (!mMutationsAllowed) { + throw new IllegalStateException("Mutations not allowed"); + } + + pipe = new WritePipe(); + mPipes.add(pipe); + } + + try { + // Use installer provided name for now; we always rename later + if (!FileUtils.isValidExtFilename(name)) { + throw new IllegalArgumentException("Invalid name: " + name); + } + final File target = new File(sessionDir, name); + + final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), + OsConstants.O_CREAT | OsConstants.O_WRONLY, 00700); + + // If caller specified a total length, allocate it for them. Free up + // cache space to grow, if needed. + if (lengthBytes > 0) { + final StructStat stat = Libcore.os.fstat(targetFd); + final long deltaBytes = lengthBytes - stat.st_size; + if (deltaBytes > 0) { + mPm.freeStorage(deltaBytes); + } + Libcore.os.posix_fallocate(targetFd, 0, lengthBytes); + } + + if (offsetBytes > 0) { + Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); + } + + pipe.setTargetFd(targetFd); + pipe.start(); + return pipe.getWriteFd(); + + } catch (ErrnoException e) { + throw new IllegalStateException("Failed to write", e); + } catch (IOException e) { + throw new IllegalStateException("Failed to write", e); + } + } + + @Override + public void install(IPackageInstallObserver2 observer) { + Preconditions.checkNotNull(observer); + mHandler.obtainMessage(MSG_INSTALL, observer).sendToTarget(); + } + + private void installLocked() throws InstallFailedException { + if (mInvalid) { + throw new InstallFailedException(INSTALL_FAILED_ALREADY_EXISTS, "Invalid session"); + } + + // Verify that all writers are hands-off + if (mMutationsAllowed) { + for (WritePipe pipe : mPipes) { + if (!pipe.isClosed()) { + throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED, + "Files still open"); + } + } + mMutationsAllowed = false; + + // TODO: persist disabled mutations before going forward, since + // beyond this point we may have hardlinks to the valid install + } + + // Verify that stage looks sane with respect to existing application. + // This currently only ensures packageName, versionCode, and certificate + // consistency. + validateInstallLocked(); + + Preconditions.checkNotNull(mPackageName); + Preconditions.checkNotNull(mSignatures); + + if (!mVerifierConfirmed) { + // TODO: async communication with verifier + // when they confirm, we'll kick off another install() pass + mVerifierConfirmed = true; + } + + if (!mPermissionsConfirmed) { + // TODO: async confirm permissions with user + // when they confirm, we'll kick off another install() pass + mPermissionsConfirmed = true; + } + + // Unpack any native libraries contained in this session + unpackNativeLibraries(); + + // Inherit any packages and native libraries from existing install that + // haven't been overridden. + if (!params.fullInstall) { + spliceExistingFilesIntoStage(); + } + + // TODO: for ASEC based applications, grow and stream in packages + + // We've reached point of no return; call into PMS to install the stage. + // Regardless of success or failure we always destroy session. + final IPackageInstallObserver2 remoteObserver = mRemoteObserver; + final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { + @Override + public void packageInstalled(String basePackageName, Bundle extras, int returnCode) + throws RemoteException { + destroy(); + remoteObserver.packageInstalled(basePackageName, extras, returnCode); + } + }; + + mPm.installStage(mPackageName, this.sessionDir, localObserver, params.installFlags); + } + + /** + * Validate install by confirming that all application packages are have + * consistent package name, version code, and signing certificates. + * <p> + * Renames package files in stage to match split names defined inside. + */ + private void validateInstallLocked() throws InstallFailedException { + mPackageName = null; + mVersionCode = -1; + mSignatures = null; + + final File[] files = sessionDir.listFiles(); + if (ArrayUtils.isEmpty(files)) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, "No packages staged"); + } + + final ArraySet<String> seenSplits = new ArraySet<>(); + + // Verify that all staged packages are internally consistent + for (File file : files) { + final PackageLite info = PackageParser.parsePackageLite(file.getAbsolutePath(), + PackageParser.PARSE_GET_SIGNATURES); + if (info == null) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, + "Failed to parse " + file); + } + + if (!seenSplits.add(info.splitName)) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, + "Split " + info.splitName + " was defined multiple times"); + } + + // Use first package to define unknown values + if (mPackageName != null) { + mPackageName = info.packageName; + mVersionCode = info.versionCode; + } + if (mSignatures != null) { + mSignatures = info.signatures; + } + + assertPackageConsistent(String.valueOf(file), info.packageName, info.versionCode, + info.signatures); + + // Take this opportunity to enforce uniform naming + final String name; + if (info.splitName == null) { + name = info.packageName + ".apk"; + } else { + name = info.packageName + "-" + info.splitName + ".apk"; + } + if (!FileUtils.isValidExtFilename(name)) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, + "Invalid filename: " + name); + } + if (!file.getName().equals(name)) { + file.renameTo(new File(file.getParentFile(), name)); + } + } + + // TODO: shift package signature verification to installer; we're + // currently relying on PMS to do this. + // TODO: teach about compatible upgrade keysets. + + if (params.fullInstall) { + // Full installs must include a base package + if (!seenSplits.contains(null)) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, + "Full install must include a base package"); + } + + } else { + // Partial installs must be consistent with existing install. + final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId); + if (app == null) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, + "Missing existing base package for " + mPackageName); + } + + final PackageLite info = PackageParser.parsePackageLite(app.sourceDir, + PackageParser.PARSE_GET_SIGNATURES); + if (info == null) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, + "Failed to parse existing base " + app.sourceDir); + } + + assertPackageConsistent("Existing base", info.packageName, info.versionCode, + info.signatures); + } + } + + private void assertPackageConsistent(String tag, String packageName, int versionCode, + Signature[] signatures) throws InstallFailedException { + if (!mPackageName.equals(packageName)) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, tag + " package " + + packageName + " inconsistent with " + mPackageName); + } + if (mVersionCode != versionCode) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, tag + + " version code " + versionCode + " inconsistent with " + + mVersionCode); + } + if (!Signature.areExactMatch(mSignatures, signatures)) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, + tag + " signatures are inconsistent"); + } + } + + /** + * Application is already installed; splice existing files that haven't been + * overridden into our stage. + */ + private void spliceExistingFilesIntoStage() throws InstallFailedException { + final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId); + final File existingDir = new File(app.sourceDir).getParentFile(); + + try { + linkTreeIgnoringExisting(existingDir, sessionDir); + } catch (ErrnoException e) { + throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR, + "Failed to splice into stage"); + } + } + + /** + * Recursively hard link all files from source directory tree to target. + * When a file already exists in the target tree, it leaves that file + * intact. + */ + private void linkTreeIgnoringExisting(File sourceDir, File targetDir) throws ErrnoException { + final File[] sourceContents = sourceDir.listFiles(); + if (ArrayUtils.isEmpty(sourceContents)) return; + + for (File sourceFile : sourceContents) { + final File targetFile = new File(targetDir, sourceFile.getName()); + + if (sourceFile.isDirectory()) { + targetFile.mkdir(); + linkTreeIgnoringExisting(sourceFile, targetFile); + } else { + Libcore.os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath()); + } + } + } + + private void unpackNativeLibraries() throws InstallFailedException { + final File libDir = new File(sessionDir, "lib"); + + if (!libDir.mkdir()) { + throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR, + "Failed to create " + libDir); + } + + try { + Libcore.os.chmod(libDir.getAbsolutePath(), 0755); + } catch (ErrnoException e) { + throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR, + "Failed to prepare " + libDir + ": " + e); + } + + if (!SELinux.restorecon(libDir)) { + throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR, + "Failed to set context on " + libDir); + } + + // Unpack all native libraries under stage + final File[] files = sessionDir.listFiles(); + if (ArrayUtils.isEmpty(files)) { + throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, "No packages staged"); + } + + for (File file : files) { + final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(file); + try { + final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle, + Build.SUPPORTED_ABIS); + if (abiIndex >= 0) { + int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libDir, + Build.SUPPORTED_ABIS[abiIndex]); + if (copyRet != INSTALL_SUCCEEDED) { + throw new InstallFailedException(copyRet, + "Failed to copy native libraries for " + file); + } + } else if (abiIndex != PackageManager.NO_NATIVE_LIBRARIES) { + throw new InstallFailedException(abiIndex, + "Failed to copy native libraries for " + file); + } + } finally { + handle.close(); + } + } + } + + @Override + public void destroy() { + try { + synchronized (mLock) { + mInvalid = true; + } + FileUtils.deleteContents(sessionDir); + sessionDir.delete(); + } finally { + mCallback.onSessionInvalid(this); + } + } + + private static class WritePipe extends Thread { + private final ParcelFileDescriptor[] mPipe; + + private FileDescriptor mTargetFd; + + private volatile boolean mClosed; + + public WritePipe() { + try { + mPipe = ParcelFileDescriptor.createPipe(); + } catch (IOException e) { + throw new IllegalStateException("Failed to create pipe"); + } + } + + public boolean isClosed() { + return mClosed; + } + + public void setTargetFd(FileDescriptor targetFd) { + mTargetFd = targetFd; + } + + public ParcelFileDescriptor getWriteFd() { + return mPipe[1]; + } + + @Override + public void run() { + FileInputStream in = null; + FileOutputStream out = null; + try { + // TODO: look at switching to sendfile(2) to speed up + in = new FileInputStream(mPipe[0].getFileDescriptor()); + out = new FileOutputStream(mTargetFd); + Streams.copy(in, out); + } catch (IOException e) { + Slog.w(TAG, "Failed to stream data: " + e); + } finally { + IoUtils.closeQuietly(mPipe[0]); + IoUtils.closeQuietly(mTargetFd); + mClosed = true; + } + } + } + + private class InstallFailedException extends Exception { + private final int error; + + public InstallFailedException(int error, String detailMessage) { + super(detailMessage); + this.error = error; + } + } +} diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d1333b2..f9eabcd 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.Manifest.permission.GRANT_REVOKE_PERMISSIONS; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; +import static android.Manifest.permission.INSTALL_PACKAGES; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; @@ -59,6 +60,7 @@ import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; +import android.app.PackageInstallObserver; import android.app.admin.IDevicePolicyManager; import android.app.backup.IBackupManager; import android.content.BroadcastReceiver; @@ -78,6 +80,7 @@ import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageInstallObserver2; +import android.content.pm.IPackageInstaller; import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; @@ -86,6 +89,7 @@ import android.content.pm.ManifestDigest; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; +import android.content.pm.PackageInstallerParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageParser; @@ -157,7 +161,6 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; -import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.text.SimpleDateFormat; @@ -179,6 +182,7 @@ import java.util.concurrent.atomic.AtomicLong; import dalvik.system.DexFile; import dalvik.system.StaleDexCacheError; import dalvik.system.VMRuntime; + import libcore.io.IoUtils; /** @@ -351,6 +355,8 @@ public class PackageManagerService extends IPackageManager.Stub { // apps. final File mDrmAppPrivateInstallDir; + final File mAppStagingDir; + // ---------------------------------------------------------------- // Lock for state used when installing and doing other long running @@ -457,6 +463,8 @@ public class PackageManagerService extends IPackageManager.Stub { final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<PackageVerificationState>(); + final PackageInstallerService mInstallerService; + HashSet<PackageParser.Package> mDeferredDexOpt = null; /** Token for keys in mPendingVerification. */ @@ -1339,6 +1347,7 @@ public class PackageManagerService extends IPackageManager.Stub { mAsecInternalPath = new File(dataDir, "app-asec").getPath(); mUserAppDataDir = new File(dataDir, "user"); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); + mAppStagingDir = new File(dataDir, "app-staging"); sUserManager = new UserManagerService(context, this, mInstallLock, mPackages); @@ -1701,14 +1710,17 @@ public class PackageManagerService extends IPackageManager.Stub { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); - // Now after opening every single application zip, make sure they - // are all flushed. Not really needed, but keeps things nice and - // tidy. - Runtime.getRuntime().gc(); mRequiredVerifierPackage = getRequiredVerifierLPr(); } // synchronized (mPackages) } // synchronized (mInstallLock) + + mInstallerService = new PackageInstallerService(context, this, mAppStagingDir); + + // Now after opening every single application zip, make sure they + // are all flushed. Not really needed, but keeps things nice and + // tidy. + Runtime.getRuntime().gc(); } private static void pruneDexFiles(File cacheDir) { @@ -2252,7 +2264,8 @@ public class PackageManagerService extends IPackageManager.Stub { if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) { return null; } - pkg = new PackageParser.Package(packageName); + // TODO: teach about reading split name + pkg = new PackageParser.Package(packageName, null); pkg.applicationInfo.packageName = packageName; pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY; pkg.applicationInfo.publicSourceDir = ps.resourcePathString; @@ -2260,7 +2273,7 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.applicationInfo.dataDir = getDataPathForPackage(packageName, 0).getPath(); pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString; - pkg.applicationInfo.requiredCpuAbi = ps.requiredCpuAbiString; + pkg.applicationInfo.cpuAbi = ps.cpuAbiString; } return generatePackageInfo(pkg, flags, userId); } @@ -2350,6 +2363,14 @@ public class PackageManagerService extends IPackageManager.Stub { }); } + void freeStorage(long freeStorageSize) throws IOException { + synchronized (mInstallLock) { + if (mInstaller.freeCache(freeStorageSize) < 0) { + throw new IOException("Failed to free enough space"); + } + } + } + @Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { if (!sUserManager.exists(userId)) return null; @@ -2533,10 +2554,9 @@ public class PackageManagerService extends IPackageManager.Stub { * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller. * @param message the message to log on security exception - * @return */ - private void enforceCrossUserPermission(int callingUid, int userId, - boolean requireFullPermission, String message) { + void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, + String message) { if (userId < 0) { throw new IllegalArgumentException("Invalid userId " + userId); } @@ -4683,8 +4703,8 @@ public class PackageManagerService extends IPackageManager.Stub { private String getAppInstructionSet(ApplicationInfo info) { String instructionSet = getPreferredInstructionSet(); - if (info.requiredCpuAbi != null) { - instructionSet = VMRuntime.getInstructionSet(info.requiredCpuAbi); + if (info.cpuAbi != null) { + instructionSet = VMRuntime.getInstructionSet(info.cpuAbi); } return instructionSet; @@ -4693,8 +4713,8 @@ public class PackageManagerService extends IPackageManager.Stub { private String getAppInstructionSetFromSettings(PackageSetting ps) { String instructionSet = getPreferredInstructionSet(); - if (ps.requiredCpuAbiString != null) { - instructionSet = VMRuntime.getInstructionSet(ps.requiredCpuAbiString); + if (ps.cpuAbiString != null) { + instructionSet = VMRuntime.getInstructionSet(ps.cpuAbiString); } return instructionSet; @@ -5082,7 +5102,7 @@ public class PackageManagerService extends IPackageManager.Stub { // the PkgSetting exists already and doesn't have to be created. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryDir, - pkg.applicationInfo.requiredCpuAbi, + pkg.applicationInfo.cpuAbi, pkg.applicationInfo.flags, user, false); if (pkgSetting == null) { Slog.w(TAG, "Creating application package " + pkg.packageName + " failed"); @@ -5410,9 +5430,9 @@ public class PackageManagerService extends IPackageManager.Stub { // We've successfully copied native libraries across, so we make a // note of what ABI we're using if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) { - pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_ABIS[copyRet]; + pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[copyRet]; } else { - pkg.applicationInfo.requiredCpuAbi = null; + pkg.applicationInfo.cpuAbi = null; } } catch (IOException e) { Slog.e(TAG, "Unable to copy native libraries", e); @@ -5430,12 +5450,12 @@ public class PackageManagerService extends IPackageManager.Stub { final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile); final int abi = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_ABIS); if (abi >= 0) { - pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_ABIS[abi]; + pkg.applicationInfo.cpuAbi = Build.SUPPORTED_ABIS[abi]; } else if (abi == PackageManager.NO_NATIVE_LIBRARIES) { // Note that (non upgraded) system apps will not have any native // libraries bundled in their APK, but we're guaranteed not to be // such an app at this point. - pkg.applicationInfo.requiredCpuAbi = null; + pkg.applicationInfo.cpuAbi = null; } else { mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; return null; @@ -5945,8 +5965,8 @@ public class PackageManagerService extends IPackageManager.Stub { String requiredInstructionSet = null; PackageSetting requirer = null; for (PackageSetting ps : packagesForUser) { - if (ps.requiredCpuAbiString != null) { - final String instructionSet = VMRuntime.getInstructionSet(ps.requiredCpuAbiString); + if (ps.cpuAbiString != null) { + final String instructionSet = VMRuntime.getInstructionSet(ps.cpuAbiString); if (requiredInstructionSet != null) { if (!instructionSet.equals(requiredInstructionSet)) { // We have a mismatch between instruction sets (say arm vs arm64). @@ -5974,11 +5994,11 @@ public class PackageManagerService extends IPackageManager.Stub { if (requiredInstructionSet != null) { for (PackageSetting ps : packagesForUser) { - if (ps.requiredCpuAbiString == null) { - ps.requiredCpuAbiString = requirer.requiredCpuAbiString; + if (ps.cpuAbiString == null) { + ps.cpuAbiString = requirer.cpuAbiString; if (ps.pkg != null) { - ps.pkg.applicationInfo.requiredCpuAbi = requirer.requiredCpuAbiString; - Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + ps.requiredCpuAbiString); + ps.pkg.applicationInfo.cpuAbi = requirer.cpuAbiString; + Slog.i(TAG, "Adjusting ABI for : " + ps.name + " to " + ps.cpuAbiString); if (doDexOpt) { performDexOptLI(ps.pkg, forceDexOpt, deferDexOpt, true); mInstaller.rmdex(ps.codePathString, getPreferredInstructionSet()); @@ -6081,15 +6101,15 @@ public class PackageManagerService extends IPackageManager.Stub { // Assume that the bundled native libraries always correspond to the // most preferred 32 or 64 bit ABI. if (lib64.exists()) { - pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; - pkgSetting.requiredCpuAbiString = Build.SUPPORTED_64_BIT_ABIS[0]; + pkg.applicationInfo.cpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + pkgSetting.cpuAbiString = Build.SUPPORTED_64_BIT_ABIS[0]; } else if (lib.exists()) { - pkg.applicationInfo.requiredCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; - pkgSetting.requiredCpuAbiString = Build.SUPPORTED_32_BIT_ABIS[0]; + pkg.applicationInfo.cpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + pkgSetting.cpuAbiString = Build.SUPPORTED_32_BIT_ABIS[0]; } else { // This is the case where the app has no native code. - pkg.applicationInfo.requiredCpuAbi = null; - pkgSetting.requiredCpuAbiString = null; + pkg.applicationInfo.cpuAbi = null; + pkgSetting.cpuAbiString = null; } } @@ -7734,6 +7754,16 @@ public class PackageManagerService extends IPackageManager.Stub { } } + void installStage(String basePackageName, File stageDir, IPackageInstallObserver2 observer, + int flags) { + // TODO: install stage! + try { + observer.packageInstalled(basePackageName, null, + PackageManager.INSTALL_FAILED_INTERNAL_ERROR); + } catch (RemoteException ignored) { + } + } + /** * @hide */ @@ -7777,7 +7807,7 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.INSTALL_SUCCEEDED; } - private boolean isUserRestricted(int userId, String restrictionKey) { + boolean isUserRestricted(int userId, String restrictionKey) { Bundle restrictions = sUserManager.getUserRestrictions(userId); if (restrictions.getBoolean(restrictionKey, false)) { Log.w(TAG, "User is restricted: " + restrictionKey); @@ -12900,4 +12930,9 @@ public class PackageManagerService extends IPackageManager.Stub { Binder.restoreCallingIdentity(token); } } + + @Override + public IPackageInstaller getPackageInstaller() { + return mInstallerService; + } } diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 15df3d2..284da99 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -30,8 +30,8 @@ final class PackageSetting extends PackageSettingBase { SharedUserSetting sharedUser; PackageSetting(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, String requiredCpuAbiString, int pVersionCode, int pkgFlags) { - super(name, realName, codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString, pVersionCode, + String nativeLibraryPathString, String cpuAbiString, int pVersionCode, int pkgFlags) { + super(name, realName, codePath, resourcePath, nativeLibraryPathString, cpuAbiString, pVersionCode, pkgFlags); } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index c8af9d1..7fee372 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -50,7 +50,7 @@ class PackageSettingBase extends GrantedPermissions { File resourcePath; String resourcePathString; String nativeLibraryPathString; - String requiredCpuAbiString; + String cpuAbiString; long timeStamp; long firstInstallTime; long lastUpdateTime; @@ -78,11 +78,11 @@ class PackageSettingBase extends GrantedPermissions { /* package name of the app that installed this package */ String installerPackageName; PackageSettingBase(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, String requiredCpuAbiString, int pVersionCode, int pkgFlags) { + String nativeLibraryPathString, String cpuAbiString, int pVersionCode, int pkgFlags) { super(pkgFlags); this.name = name; this.realName = realName; - init(codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString, pVersionCode); + init(codePath, resourcePath, nativeLibraryPathString, cpuAbiString, pVersionCode); } /** @@ -99,7 +99,7 @@ class PackageSettingBase extends GrantedPermissions { resourcePath = base.resourcePath; resourcePathString = base.resourcePathString; nativeLibraryPathString = base.nativeLibraryPathString; - requiredCpuAbiString = base.requiredCpuAbiString; + cpuAbiString = base.cpuAbiString; timeStamp = base.timeStamp; firstInstallTime = base.firstInstallTime; lastUpdateTime = base.lastUpdateTime; @@ -133,7 +133,7 @@ class PackageSettingBase extends GrantedPermissions { this.resourcePath = resourcePath; this.resourcePathString = resourcePath.toString(); this.nativeLibraryPathString = nativeLibraryPathString; - this.requiredCpuAbiString = requiredCpuAbiString; + this.cpuAbiString = requiredCpuAbiString; this.versionCode = pVersionCode; } @@ -164,7 +164,7 @@ class PackageSettingBase extends GrantedPermissions { grantedPermissions = base.grantedPermissions; gids = base.gids; - requiredCpuAbiString = base.requiredCpuAbiString; + cpuAbiString = base.cpuAbiString; timeStamp = base.timeStamp; firstInstallTime = base.firstInstallTime; lastUpdateTime = base.lastUpdateTime; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 599d2a7..d37b240 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -260,10 +260,10 @@ final class Settings { PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, String requiredCpuAbiString, int pkgFlags, UserHandle user, boolean add) { + String nativeLibraryPathString, String cpuAbiString, int pkgFlags, UserHandle user, boolean add) { final String name = pkg.packageName; PackageSetting p = getPackageLPw(name, origPackage, realName, sharedUser, codePath, - resourcePath, nativeLibraryPathString, requiredCpuAbiString, pkg.mVersionCode, pkgFlags, + resourcePath, nativeLibraryPathString, cpuAbiString, pkg.mVersionCode, pkgFlags, user, add, true /* allowInstall */); return p; } @@ -350,7 +350,7 @@ final class Settings { p.pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath, - p.nativeLibraryPathString, p.requiredCpuAbiString, p.appId, p.versionCode, p.pkgFlags); + p.nativeLibraryPathString, p.cpuAbiString, p.appId, p.versionCode, p.pkgFlags); mDisabledSysPackages.remove(name); return ret; } @@ -364,7 +364,7 @@ final class Settings { } PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath, - String nativeLibraryPathString, String requiredCpuAbiString, int uid, int vc, int pkgFlags) { + String nativeLibraryPathString, String cpuAbiString, int uid, int vc, int pkgFlags) { PackageSetting p = mPackages.get(name); if (p != null) { if (p.appId == uid) { @@ -374,7 +374,7 @@ final class Settings { "Adding duplicate package, keeping first: " + name); return null; } - p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, requiredCpuAbiString, + p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString, cpuAbiString, vc, pkgFlags); p.appId = uid; if (addUserIdLPw(uid, p, name)) { @@ -443,11 +443,11 @@ final class Settings { private PackageSetting getPackageLPw(String name, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String nativeLibraryPathString, String requiredCpuAbiString, int vc, int pkgFlags, + String nativeLibraryPathString, String cpuAbiString, int vc, int pkgFlags, UserHandle installUser, boolean add, boolean allowInstall) { PackageSetting p = mPackages.get(name); if (p != null) { - p.requiredCpuAbiString = requiredCpuAbiString; + p.cpuAbiString = cpuAbiString; if (!p.codePath.equals(codePath)) { // Check to see if its a disabled system app if ((p.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { @@ -491,7 +491,7 @@ final class Settings { if (origPackage != null) { // We are consuming the data from an existing package. p = new PackageSetting(origPackage.name, name, codePath, resourcePath, - nativeLibraryPathString, requiredCpuAbiString, vc, pkgFlags); + nativeLibraryPathString, cpuAbiString, vc, pkgFlags); if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + name + " is adopting original package " + origPackage.name); // Note that we will retain the new package's signature so @@ -508,7 +508,7 @@ final class Settings { p.setTimeStamp(codePath.lastModified()); } else { p = new PackageSetting(name, realName, codePath, resourcePath, - nativeLibraryPathString, requiredCpuAbiString, vc, pkgFlags); + nativeLibraryPathString, cpuAbiString, vc, pkgFlags); p.setTimeStamp(codePath.lastModified()); p.sharedUser = sharedUser; // If this is not a system app, it starts out stopped. @@ -635,7 +635,7 @@ final class Settings { p.nativeLibraryPathString = nativeLibraryPath; } // Update the required Cpu Abi - p.requiredCpuAbiString = pkg.applicationInfo.requiredCpuAbi; + p.cpuAbiString = pkg.applicationInfo.cpuAbi; // Update version code if needed if (pkg.mVersionCode != p.versionCode) { p.versionCode = pkg.mVersionCode; @@ -1690,8 +1690,8 @@ final class Settings { if (pkg.nativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); } - if (pkg.requiredCpuAbiString != null) { - serializer.attribute(null, "requiredCpuAbi", pkg.requiredCpuAbiString); + if (pkg.cpuAbiString != null) { + serializer.attribute(null, "requiredCpuAbi", pkg.cpuAbiString); } if (pkg.sharedUser == null) { serializer.attribute(null, "userId", Integer.toString(pkg.appId)); @@ -1735,8 +1735,8 @@ final class Settings { if (pkg.nativeLibraryPathString != null) { serializer.attribute(null, "nativeLibraryPath", pkg.nativeLibraryPathString); } - if (pkg.requiredCpuAbiString != null) { - serializer.attribute(null, "requiredCpuAbi", pkg.requiredCpuAbiString); + if (pkg.cpuAbiString != null) { + serializer.attribute(null, "requiredCpuAbi", pkg.cpuAbiString); } serializer.attribute(null, "flags", Integer.toString(pkg.pkgFlags)); serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp)); @@ -2023,7 +2023,7 @@ final class Settings { if (idObj != null && idObj instanceof SharedUserSetting) { PackageSetting p = getPackageLPw(pp.name, null, pp.realName, (SharedUserSetting) idObj, pp.codePath, pp.resourcePath, - pp.nativeLibraryPathString, pp.requiredCpuAbiString, pp.versionCode, pp.pkgFlags, + pp.nativeLibraryPathString, pp.cpuAbiString, pp.versionCode, pp.pkgFlags, null, true /* add */, false /* allowInstall */); if (p == null) { PackageManagerService.reportSettingsProblem(Log.WARN, @@ -2443,7 +2443,7 @@ final class Settings { String codePathStr = parser.getAttributeValue(null, "codePath"); String resourcePathStr = parser.getAttributeValue(null, "resourcePath"); String nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - String requiredCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); + String cpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); if (resourcePathStr == null) { resourcePathStr = codePathStr; @@ -2464,7 +2464,7 @@ final class Settings { pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED; } PackageSetting ps = new PackageSetting(name, realName, codePathFile, - new File(resourcePathStr), nativeLibraryPathStr, requiredCpuAbiString, versionCode, pkgFlags); + new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, versionCode, pkgFlags); String timeStampStr = parser.getAttributeValue(null, "ft"); if (timeStampStr != null) { try { @@ -2531,7 +2531,7 @@ final class Settings { String codePathStr = null; String resourcePathStr = null; String nativeLibraryPathStr = null; - String requiredCpuAbiString = null; + String cpuAbiString = null; String systemStr = null; String installerPackageName = null; String uidError = null; @@ -2551,7 +2551,7 @@ final class Settings { codePathStr = parser.getAttributeValue(null, "codePath"); resourcePathStr = parser.getAttributeValue(null, "resourcePath"); nativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); - requiredCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); + cpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); version = parser.getAttributeValue(null, "version"); if (version != null) { @@ -2629,7 +2629,7 @@ final class Settings { + parser.getPositionDescription()); } else if (userId > 0) { packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), - new File(resourcePathStr), nativeLibraryPathStr, requiredCpuAbiString, userId, versionCode, + new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, userId, versionCode, pkgFlags); if (PackageManagerService.DEBUG_SETTINGS) Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId=" @@ -2647,7 +2647,7 @@ final class Settings { userId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; if (userId > 0) { packageSetting = new PendingPackage(name.intern(), realName, new File( - codePathStr), new File(resourcePathStr), nativeLibraryPathStr, requiredCpuAbiString, userId, + codePathStr), new File(resourcePathStr), nativeLibraryPathStr, cpuAbiString, userId, versionCode, pkgFlags); packageSetting.setTimeStamp(timeStamp); packageSetting.firstInstallTime = firstInstallTime; @@ -2676,7 +2676,7 @@ final class Settings { packageSetting.uidError = "true".equals(uidError); packageSetting.installerPackageName = installerPackageName; packageSetting.nativeLibraryPathString = nativeLibraryPathStr; - packageSetting.requiredCpuAbiString = requiredCpuAbiString; + packageSetting.cpuAbiString = cpuAbiString; // Handle legacy string here for single-user mode final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); if (enabledStr != null) { @@ -3176,7 +3176,7 @@ final class Settings { pw.print(prefix); pw.print(" codePath="); pw.println(ps.codePathString); pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.resourcePathString); pw.print(prefix); pw.print(" nativeLibraryPath="); pw.println(ps.nativeLibraryPathString); - pw.print(prefix); pw.print(" requiredCpuAbi="); pw.println(ps.requiredCpuAbiString); + pw.print(prefix); pw.print(" requiredCpuAbi="); pw.println(ps.cpuAbiString); pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode); if (ps.pkg != null) { pw.print(" targetSdk="); pw.print(ps.pkg.applicationInfo.targetSdkVersion); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 60212bf..131d05b 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -40,6 +40,7 @@ import android.os.Handler; import android.os.IUserManager; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.util.AtomicFile; @@ -50,6 +51,7 @@ import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.Xml; +import com.android.internal.app.IAppOpsService; import com.android.internal.content.PackageMonitor; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; @@ -162,6 +164,8 @@ public class UserManagerService extends IUserManager.Stub { private int mNextSerialNumber; private int mUserVersion = 0; + private IAppOpsService mAppOpsService; + private static UserManagerService sInstance; public static UserManagerService getInstance() { @@ -236,6 +240,15 @@ public class UserManagerService extends IUserManager.Stub { void systemReady() { mUserPackageMonitor.register(mContext, null, UserHandle.ALL, false); userForeground(UserHandle.USER_OWNER); + mAppOpsService = IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); + for (int i = 0; i < mUserIds.length; ++i) { + try { + mAppOpsService.setUserRestrictions(mUserRestrictions.get(mUserIds[i]), mUserIds[i]); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions"); + } + } } @Override @@ -482,6 +495,14 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mPackagesLock) { mUserRestrictions.get(userId).clear(); mUserRestrictions.get(userId).putAll(restrictions); + long token = Binder.clearCallingIdentity(); + try { + mAppOpsService.setUserRestrictions(mUserRestrictions.get(userId), userId); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions"); + } finally { + Binder.restoreCallingIdentity(token); + } writeUserLocked(mUsers.get(userId)); } } @@ -1116,6 +1137,11 @@ public class UserManagerService extends IUserManager.Stub { return false; } mRemovingUserIds.put(userHandle, true); + try { + mAppOpsService.removeUser(userHandle); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e); + } // Set this to a partially created user, so that the user will be purged // on next startup, in case the runtime stops now before stopping and // removing the user completely. @@ -1125,6 +1151,14 @@ public class UserManagerService extends IUserManager.Stub { user.flags |= UserInfo.FLAG_DISABLED; writeUserLocked(user); } + + if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID + && user.isManagedProfile()) { + // Send broadcast to notify system that the user removed was a + // managed user. + sendProfileRemovedBroadcast(user.profileGroupId, user.id); + } + if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); int res; try { @@ -1151,7 +1185,6 @@ public class UserManagerService extends IUserManager.Stub { // wiping the user's system directory and removing from the user list long ident = Binder.clearCallingIdentity(); try { - final boolean isManaged = getUserInfo(userHandle).isManagedProfile(); Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL, @@ -1172,11 +1205,6 @@ public class UserManagerService extends IUserManager.Stub { removeUserStateLocked(userHandle); } } - // Send broadcast to notify system that the user removed was a - // managed user. - if (isManaged) { - sendProfileRemovedBroadcast(userHandle); - } } }.start(); } @@ -1228,11 +1256,11 @@ public class UserManagerService extends IUserManager.Stub { parent.delete(); } - private void sendProfileRemovedBroadcast(int userHandle) { + private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) { Intent managedProfileIntent = new Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED); - managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(userHandle)); - // Note: This makes an assumption that the parent owner is user 0. - mContext.sendBroadcastAsUser(managedProfileIntent, UserHandle.OWNER, null); + managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId)); + mContext.sendBroadcastAsUser(managedProfileIntent, new UserHandle(parentUserId), null); } @Override diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 2d6cc7c..e244bde 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -190,14 +190,14 @@ final class Notifier { + ", workSource=" + newWorkSource); } try { - mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, monitorType, - newWorkSource, newOwnerPid, newTag, newHistoryTag, + mBatteryStats.noteChangeWakelockFromSource(workSource, ownerPid, tag, historyTag, + monitorType, newWorkSource, newOwnerPid, newTag, newHistoryTag, newMonitorType, unimportantForLogging); } catch (RemoteException ex) { // Ignore } } else { - onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource); + onWakeLockReleased(flags, tag, packageName, ownerUid, ownerPid, workSource, historyTag); onWakeLockAcquired(newFlags, newTag, newPackageName, newOwnerUid, newOwnerPid, newWorkSource, newHistoryTag); } @@ -207,7 +207,7 @@ final class Notifier { * Called when a wake lock is released. */ public void onWakeLockReleased(int flags, String tag, String packageName, - int ownerUid, int ownerPid, WorkSource workSource) { + int ownerUid, int ownerPid, WorkSource workSource, String historyTag) { if (DEBUG) { Slog.d(TAG, "onWakeLockReleased: flags=" + flags + ", tag=\"" + tag + "\", packageName=" + packageName @@ -218,9 +218,10 @@ final class Notifier { try { final int monitorType = getBatteryStatsWakeLockMonitorType(flags); if (workSource != null) { - mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, monitorType); + mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, historyTag, + monitorType); } else { - mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, monitorType); + mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, historyTag, monitorType); mAppOps.finishOperation(AppOpsManager.getToken(mAppOps), AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 03941c6..f0b7861 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -18,6 +18,7 @@ package com.android.server.power; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; +import com.android.internal.os.BackgroundThread; import com.android.server.BatteryService; import com.android.server.EventLogTags; import com.android.server.LocalServices; @@ -400,7 +401,10 @@ public final class PowerManagerService extends com.android.server.SystemService private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE; // If true, the device is in low power mode. - private static boolean mLowPowerModeEnabled; + private boolean mLowPowerModeEnabled; + + private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners + = new ArrayList<PowerManagerInternal.LowPowerModeListener>(); private native void nativeInit(); @@ -623,11 +627,24 @@ public final class PowerManagerService extends com.android.server.SystemService Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); - boolean lowPowerModeEnabled = Settings.Global.getInt(resolver, + final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver, Settings.Global.LOW_POWER_MODE, 0) != 0; if (lowPowerModeEnabled != mLowPowerModeEnabled) { powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0); mLowPowerModeEnabled = lowPowerModeEnabled; + BackgroundThread.getHandler().post(new Runnable() { + @Override + public void run() { + ArrayList<PowerManagerInternal.LowPowerModeListener> listeners; + synchronized (mLock) { + listeners = new ArrayList<PowerManagerInternal.LowPowerModeListener>( + mLowPowerModeListeners); + } + for (int i=0; i<listeners.size(); i++) { + listeners.get(i).onLowPowerModeChanged(lowPowerModeEnabled); + } + } + }); } mDirty |= DIRTY_SETTINGS; @@ -812,8 +829,9 @@ public final class PowerManagerService extends com.android.server.SystemService private void notifyWakeLockReleasedLocked(WakeLock wakeLock) { if (mSystemReady && wakeLock.mNotifiedAcquired) { wakeLock.mNotifiedAcquired = false; - mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName, - wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource); + mNotifier.onWakeLockReleased(wakeLock.mFlags, wakeLock.mTag, + wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid, + wakeLock.mWorkSource, wakeLock.mHistoryTag); } } @@ -2972,6 +2990,20 @@ public final class PowerManagerService extends com.android.server.SystemService } @Override + public boolean getLowPowerModeEnabled() { + synchronized (mLock) { + return mLowPowerModeEnabled; + } + } + + @Override + public void registerLowPowerModeObserver(LowPowerModeListener listener) { + synchronized (mLock) { + mLowPowerModeListeners.add(listener); + } + } + + @Override public void setPolicy(WindowManagerPolicy policy) { PowerManagerService.this.setPolicy(policy); } diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index a83fa87..d8d3da1 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -52,7 +52,7 @@ public class TrustAgentWrapper { // Trust state private boolean mTrusted; - private String mMessage; + private CharSequence mMessage; private final Handler mHandler = new Handler() { @Override @@ -60,7 +60,7 @@ public class TrustAgentWrapper { switch (msg.what) { case MSG_ENABLE_TRUST: mTrusted = true; - mMessage = (String) msg.obj; + mMessage = (CharSequence) msg.obj; boolean initiatedByUser = msg.arg1 != 0; // TODO: Handle handle user initiated trust changes. mTrustManagerService.updateTrust(mUserId); @@ -79,7 +79,8 @@ public class TrustAgentWrapper { private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() { - public void enableTrust(String userMessage, long durationMs, boolean initiatedByUser) { + @Override + public void grantTrust(CharSequence userMessage, long durationMs, boolean initiatedByUser) { if (DEBUG) Slog.v(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs + ", initiatedByUser = " + initiatedByUser + ")"); @@ -91,6 +92,7 @@ public class TrustAgentWrapper { } } + @Override public void revokeTrust() { if (DEBUG) Slog.v(TAG, "revokeTrust()"); mHandler.sendEmptyMessage(MSG_REVOKE_TRUST); @@ -155,7 +157,7 @@ public class TrustAgentWrapper { return mTrusted; } - public String getMessage() { + public CharSequence getMessage() { return mMessage; } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 9061f96..a39c116 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -221,8 +221,8 @@ public class TrustManagerService extends SystemService { // Drain preamble. } String nodeName = parser.getName(); - if (!"trust_agent".equals(nodeName)) { - Slog.w(TAG, "Meta-data does not start with trust_agent tag"); + if (!"trust-agent".equals(nodeName)) { + Slog.w(TAG, "Meta-data does not start with trust-agent tag"); return null; } TypedArray sa = res diff --git a/services/core/java/com/android/server/wm/ViewServer.java b/services/core/java/com/android/server/wm/ViewServer.java index a763e2c..741cee3 100644 --- a/services/core/java/com/android/server/wm/ViewServer.java +++ b/services/core/java/com/android/server/wm/ViewServer.java @@ -314,7 +314,7 @@ class ViewServer implements Runnable { out.flush(); } if (needFocusedWindowUpdate) { - out.write("FOCUS UPDATE\n"); + out.write("ACTION_FOCUS UPDATE\n"); out.flush(); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 836a19c..b61ba5c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2806,18 +2806,10 @@ public class WindowManagerService extends IWindowManager.Stub boolean configChanged; boolean surfaceChanged = false; boolean animating; + boolean hasStatusBarPermission = + mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) + == PackageManager.PERMISSION_GRANTED; - // if they don't have this permission, mask out the status bar bits - int systemUiVisibility = 0; - if (attrs != null) { - systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility); - if ((systemUiVisibility & StatusBarManager.DISABLE_MASK) != 0) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) - != PackageManager.PERMISSION_GRANTED) { - systemUiVisibility &= ~StatusBarManager.DISABLE_MASK; - } - } - } long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { @@ -2832,14 +2824,26 @@ public class WindowManagerService extends IWindowManager.Stub win.mRequestedWidth = requestedWidth; win.mRequestedHeight = requestedHeight; } - if (attrs != null && seq == win.mSeq) { - win.mSystemUiVisibility = systemUiVisibility; - } if (attrs != null) { mPolicy.adjustWindowParamsLw(attrs); } + // if they don't have the permission, mask out the status bar bits + int systemUiVisibility = 0; + if (attrs != null) { + systemUiVisibility = (attrs.systemUiVisibility|attrs.subtreeSystemUiVisibility); + if ((systemUiVisibility & StatusBarManager.DISABLE_MASK) != 0) { + if (!hasStatusBarPermission) { + systemUiVisibility &= ~StatusBarManager.DISABLE_MASK; + } + } + } + + if (attrs != null && seq == win.mSeq) { + win.mSystemUiVisibility = systemUiVisibility; + } + winAnimator.mSurfaceDestroyDeferred = (flags&WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY) != 0; diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp index 6e03993..1d111a1 100644 --- a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp +++ b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp @@ -142,7 +142,7 @@ private: // Handles incoming <Request Active Source> message. If one of logical // devices is active, it should reply with <Active Source> message. void handleRequestActiveSource(); - void handleGetOsdName(const cec_message_t& msg); + void handleGiveOsdName(const cec_message_t& msg); void handleGiveDeviceVendorID(const cec_message_t& msg); void handleGetCECVersion(const cec_message_t& msg); void handleGetMenuLanguage(const cec_message_t& msg); @@ -555,8 +555,8 @@ void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) { sendReportPhysicalAddress(msg.destination); } else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) { handleRequestActiveSource(); - } else if (opcode == CEC_MESSAGE_GET_OSD_NAME) { - handleGetOsdName(msg); + } else if (opcode == CEC_MESSAGE_GIVE_OSD_NAME) { + handleGiveOsdName(msg); } else if (opcode == CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID) { handleGiveDeviceVendorID(msg); } else if (opcode == CEC_MESSAGE_GET_CEC_VERSION) { @@ -631,7 +631,7 @@ void HdmiCecHandler::handleRequestActiveSource() { checkAndClearExceptionFromCallback(env, __FUNCTION__); } -void HdmiCecHandler::handleGetOsdName(const cec_message_t& msg) { +void HdmiCecHandler::handleGiveOsdName(const cec_message_t& msg) { if (!mOsdName.empty()) { sendSetOsdName(msg.destination, msg.initiator, mOsdName.c_str(), mOsdName.length()); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java index 1647425..674c6f4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java @@ -39,6 +39,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; +import java.util.Set; /** * Stores and restores state for the Device and Profile owners. By definition there can be @@ -137,6 +138,10 @@ public class DeviceOwner { return profileOwner != null ? profileOwner.name : null; } + Set<Integer> getProfileOwnerKeys() { + return mProfileOwners.keySet(); + } + boolean hasDeviceOwner() { return mDeviceOwner != null; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 1980d1e..a0c59cc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -19,6 +19,7 @@ package com.android.server.devicepolicy; import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; import com.android.internal.R; +import com.android.internal.app.IAppOpsService; import com.android.internal.os.storage.ExternalStorageFormatter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; @@ -237,6 +238,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } }; + private IAppOpsService mAppOpsService; + static class ActiveAdmin { private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features"; private static final String TAG_DISABLE_CAMERA = "disable-camera"; @@ -1209,6 +1212,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER); loadDeviceOwner(); } + mAppOpsService = IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); + if (mDeviceOwner.hasDeviceOwner()) { + try { + mAppOpsService.setDeviceOwner(mDeviceOwner.getDeviceOwnerPackageName()); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of DeviceOwner", e); + } + } + for (Integer i : mDeviceOwner.getProfileOwnerKeys()) { + try { + mAppOpsService.setProfileOwner(mDeviceOwner.getProfileOwnerName(i), i); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of ProfileOwner", e); + } + } } private void handlePasswordExpirationNotification(int userHandle) { @@ -2953,6 +2972,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "Trying to set device owner but device owner is already set."); } + long token = Binder.clearCallingIdentity(); + try { + mAppOpsService.setDeviceOwner(packageName); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of DeviceOwner", e); + } finally { + Binder.restoreCallingIdentity(token); + } if (mDeviceOwner == null) { // Device owner is not set and does not exist, set it. mDeviceOwner = DeviceOwner.createWithDeviceOwner(packageName, ownerName); @@ -3029,6 +3056,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new IllegalStateException( "Trying to set profile owner but user is already set-up."); } + long token = Binder.clearCallingIdentity(); + try { + mAppOpsService.setProfileOwner(packageName, userHandle); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of ProfileOwner", e); + } finally { + Binder.restoreCallingIdentity(token); + } if (mDeviceOwner == null) { // Device owner state does not exist, create it. mDeviceOwner = DeviceOwner.createWithProfileOwner(packageName, ownerName, diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 8b54264..bcbae60 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -16,6 +16,8 @@ package com.android.server.usb; +import android.alsa.AlsaCardsParser; +import android.alsa.AlsaDevicesParser; import android.content.Context; import android.content.Intent; import android.hardware.usb.UsbConfiguration; @@ -29,8 +31,6 @@ import android.os.Parcelable; import android.os.UserHandle; import android.util.Slog; -import com.android.alsascan.AlsaCardsParser; -import com.android.alsascan.AlsaDevicesParser; import com.android.internal.annotations.GuardedBy; import java.io.File; diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index a016933..3190fb0 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -33,6 +33,7 @@ import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.ManifestDigest; import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; @@ -718,4 +719,9 @@ public class MockPackageManager extends PackageManager { public void clearForwardingIntentFilters(int userIdOrig) { throw new UnsupportedOperationException(); } + + /** {@hide} */ + public PackageInstaller getPackageInstaller() { + throw new UnsupportedOperationException(); + } } diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 3e9cf43..db802c5 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -216,6 +216,15 @@ </activity> <activity + android:name="ClipOutlineActivity" + android:label="Clip/Outline"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> + + <activity android:name="DisplayListLayersActivity" android:label="Layers/Display Lists"> <intent-filter> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java index 1d0a806..e4ea936 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java @@ -109,26 +109,27 @@ public class CirclePropActivity extends Activity { mToggle = !mToggle; mRunningAnimations.add(new RenderNodeAnimator( - mX, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 400.0f : 200.0f)); + mX, mToggle ? 400.0f : 200.0f)); mRunningAnimations.add(new RenderNodeAnimator( - mY, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 600.0f : 200.0f)); + mY, mToggle ? 600.0f : 200.0f)); mRunningAnimations.add(new RenderNodeAnimator( - mRadius, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 250.0f : 150.0f)); + mRadius, mToggle ? 250.0f : 150.0f)); mRunningAnimations.add(new RenderNodeAnimator( mPaint, RenderNodeAnimator.PAINT_ALPHA, - RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 64.0f : 255.0f)); + mToggle ? 64.0f : 255.0f)); mRunningAnimations.add(new RenderNodeAnimator( mPaint, RenderNodeAnimator.PAINT_STROKE_WIDTH, - RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 5.0f : 60.0f)); + mToggle ? 5.0f : 60.0f)); TimeInterpolator interp = new OvershootInterpolator(3.0f); for (int i = 0; i < mRunningAnimations.size(); i++) { RenderNodeAnimator anim = mRunningAnimations.get(i); anim.setInterpolator(interp); + anim.setDuration(1000); anim.start(this); } diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipOutlineActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipOutlineActivity.java new file mode 100644 index 0000000..af448e8 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipOutlineActivity.java @@ -0,0 +1,87 @@ +/* + * 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.hwui; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Outline; +import android.graphics.Path; +import android.graphics.Rect; +import android.os.Bundle; +import android.widget.FrameLayout; +import android.widget.TextView; + +public class ClipOutlineActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final RegionView group = new RegionView(this); + + final TextView text = new TextView(this); + text.setText(buildText()); + group.addView(text); + + setContentView(group); + + ObjectAnimator animator = ObjectAnimator.ofFloat(group, "clipPosition", 0.0f, 1.0f); + animator.setDuration(3000); + animator.setRepeatCount(ValueAnimator.INFINITE); + animator.setRepeatMode(ValueAnimator.REVERSE); + animator.start(); + } + + private static CharSequence buildText() { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < 10; i++) { + buffer.append(LOREM_IPSUM); + } + return buffer; + } + + public static class RegionView extends FrameLayout { + private float mClipPosition = 0.0f; + private Outline mOutline = new Outline(); + private Rect mRect = new Rect(); + + public RegionView(Context c) { + super(c); + setClipToOutline(true); + } + + public float getClipPosition() { + return mClipPosition; + } + + public void setClipPosition(float clipPosition) { + mClipPosition = clipPosition; + int w = getWidth() / 2; + int h = getHeight() / 2; + + mRect.set(0, 0, w, h); + mRect.offset((int) (clipPosition * w), getHeight() / 4); + mOutline.setRoundRect(mRect, w / 2); + setOutline(mOutline); + invalidate(); + } + } + + private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sagittis molestie aliquam. Donec metus metus, laoreet nec sagittis vitae, ultricies sit amet eros. Suspendisse sed massa sit amet felis consectetur gravida. In vitae erat mi, in egestas nisl. Phasellus quis ipsum massa, at scelerisque arcu. Nam lectus est, pellentesque eget lacinia non, congue vitae augue. Aliquam erat volutpat. Pellentesque bibendum tincidunt viverra. Aliquam erat volutpat. Maecenas pretium vulputate placerat. Nulla varius elementum rutrum. Aenean mollis blandit imperdiet. Pellentesque interdum fringilla ligula."; +} diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java index 8f9cf58..b5b12d8 100644 --- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java +++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java @@ -5,6 +5,7 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; +import android.util.Log; import android.view.HardwareRenderer; import android.view.RenderNodeAnimator; import android.view.View; @@ -73,14 +74,20 @@ public class MainActivity extends Activity implements OnItemClickListener { float delta = (pos - clickedPosition) * 1.1f; if (delta == 0) delta = -1; RenderNodeAnimator animator = new RenderNodeAnimator( - RenderNodeAnimator.TRANSLATION_Y, RenderNodeAnimator.DELTA_TYPE_DELTA, dy * delta); + RenderNodeAnimator.TRANSLATION_Y, dy * delta); animator.setDuration(DURATION); + if (child == clickedView) logTranslationY(clickedView); animator.start(child); + if (child == clickedView) logTranslationY(clickedView); } //mHandler.postDelayed(mLaunchActivity, (long) (DURATION * .4)); mLaunchActivity.run(); } + private void logTranslationY(View v) { + Log.d("RTTest", "View has translationY: " + v.getTranslationY()); + } + private Runnable mLaunchActivity = new Runnable() { @Override diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index dca25e5..4e0a9fe 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -453,7 +453,7 @@ String8 getComponentName(String8 &pkgName, String8 &componentName) { return retStr; } -static void printCompatibleScreens(ResXMLTree& tree) { +static void printCompatibleScreens(ResXMLTree& tree, String8* outError) { size_t len; ResXMLTree::event_code_t code; int depth = 0; @@ -471,7 +471,12 @@ static void printCompatibleScreens(ResXMLTree& tree) { continue; } depth++; - String8 tag(tree.getElementName(&len)); + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + *outError = "failed to get XML element name (bad string pool)"; + return; + } + String8 tag(ctag16); if (tag == "screen") { int32_t screenSize = getIntegerAttribute(tree, SCREEN_SIZE_ATTR, NULL, -1); @@ -536,7 +541,12 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::END_TAG) { depth--; - String8 tag(tree.getElementName(&len)); + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + *outError = "failed to get XML element name (bad string pool)"; + return Vector<String8>(); + } + String8 tag(ctag16); if (depth == 0 && tag == serviceTagName) { withinApduService = false; @@ -544,7 +554,12 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool } else if (code == ResXMLTree::START_TAG) { depth++; - String8 tag(tree.getElementName(&len)); + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + *outError = "failed to get XML element name (bad string pool)"; + return Vector<String8>(); + } + String8 tag(ctag16); if (depth == 1) { if (tag == serviceTagName) { @@ -711,7 +726,12 @@ int doDump(Bundle* bundle) continue; } depth++; - String8 tag(tree.getElementName(&len)); + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); + goto bail; + } + String8 tag(ctag16); //printf("Depth %d tag %s\n", depth, tag.string()); if (depth == 1) { if (tag != "manifest") { @@ -970,7 +990,13 @@ int doDump(Bundle* bundle) continue; } depth++; - String8 tag(tree.getElementName(&len)); + + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); + goto bail; + } + String8 tag(ctag16); //printf("Depth %d, %s\n", depth, tag.string()); if (depth == 1) { if (tag != "manifest") { @@ -1297,7 +1323,12 @@ int doDump(Bundle* bundle) goto bail; } } else if (tag == "compatible-screens") { - printCompatibleScreens(tree); + printCompatibleScreens(tree, &error); + if (error != "") { + fprintf(stderr, "ERROR getting compatible screens: %s\n", + error.string()); + goto bail; + } depth--; } else if (tag == "package-verifier") { String8 name = getAttribute(tree, NAME_ATTR, &error); @@ -1423,7 +1454,7 @@ int doDump(Bundle* bundle) " service '%s': %s\n", serviceName.string(), error.string()); } } else if (bundle->getIncludeMetaData() && tag == "meta-data") { - String8 metaDataName = getAttribute(tree, NAME_ATTR, &error); + String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute for " "meta-data:%s\n", error.string()); diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index aede236..56c0de9 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -757,7 +757,8 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawRoundRect(long nativeCanvas, - final RectF rect, final float rx, final float ry, long paint) { + final float left, final float top, final float right, final float bottom, + final float rx, final float ry, long paint) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @@ -769,16 +770,16 @@ public final class Canvas_Delegate { if (style == Paint.Style.FILL.nativeInt || style == Paint.Style.FILL_AND_STROKE.nativeInt) { graphics.fillRoundRect( - (int)rect.left, (int)rect.top, - (int)rect.width(), (int)rect.height(), + (int)left, (int)top, + (int)(right - left), (int)(bottom - top), (int)rx, (int)ry); } if (style == Paint.Style.STROKE.nativeInt || style == Paint.Style.FILL_AND_STROKE.nativeInt) { graphics.drawRoundRect( - (int)rect.left, (int)rect.top, - (int)rect.width(), (int)rect.height(), + (int)left, (int)top, + (int)(right - left), (int)(bottom - top), (int)rx, (int)ry); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 25eaaf5..83df745 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -420,7 +420,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy, + /*package*/ static void native_setShadowLayer(long paint, float radius, float dx, float dy, int color) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -428,6 +428,14 @@ public class Paint_Delegate { } @LayoutlibDelegate + /*package*/ static boolean native_hasShadowLayer(long paint) { + // FIXME + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.hasShadowLayer is not supported.", null, null /*data*/); + return false; + } + + @LayoutlibDelegate /*package*/ static boolean isElegantTextHeight(Paint thisPaint) { return false; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index b235408..0ec7115 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -318,16 +318,6 @@ public final class Path_Delegate { } @LayoutlibDelegate - /*package*/ static void native_addRect(long nPath, RectF rect, int dir) { - Path_Delegate pathDelegate = sManager.getDelegate(nPath); - if (pathDelegate == null) { - return; - } - - pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir); - } - - @LayoutlibDelegate /*package*/ static void native_addRect(long nPath, float left, float top, float right, float bottom, int dir) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); @@ -339,14 +329,15 @@ public final class Path_Delegate { } @LayoutlibDelegate - /*package*/ static void native_addOval(long nPath, RectF oval, int dir) { + /*package*/ static void native_addOval(long nPath, float left, float top, float right, + float bottom, int dir) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { return; } pathDelegate.mPath.append(new Ellipse2D.Float( - oval.left, oval.top, oval.width(), oval.height()), false); + left, top, right - left, bottom - top), false); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java index ff82a5e..1e7564e 100644 --- a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java +++ b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java @@ -1,5 +1,5 @@ /* - * 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. @@ -23,26 +23,29 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.util.Map; /** - * Delegate implementing the native methods of android.os.Build + * Delegate implementing the native methods of android.os.SystemProperties * - * Through the layoutlib_create tool, the original native methods of Build have been replaced - * by calls to methods of the same name in this delegate class. + * Through the layoutlib_create tool, the original native methods of SystemProperties have been + * replaced by calls to methods of the same name in this delegate class. * * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager} * around to map int to instance of the delegate. - * */ -public class Build_Delegate { +public class SystemProperties_Delegate { @LayoutlibDelegate - /*package*/ static String getString(String property) { + /*package*/ static String native_get(String key) { + return native_get(key, ""); + } + + @LayoutlibDelegate + /*package*/ static String native_get(String key, String def) { Map<String, String> properties = Bridge.getPlatformProperties(); - String value = properties.get(property); + String value = properties.get(key); if (value != null) { return value; } - return Build.UNKNOWN; + return def; } - } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 59979ce..bb72a1e 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -133,7 +133,6 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.BitmapFactory#finishDecode", "android.os.Handler#sendMessageAtTime", "android.os.HandlerThread#run", - "android.os.Build#getString", "android.text.format.DateFormat#is24HourFormat", "android.view.Choreographer#getRefreshRate", "android.view.Display#updateDisplayInfoLocked", @@ -148,6 +147,7 @@ public final class CreateInfo implements ICreateInfo { "com.android.internal.view.menu.MenuBuilder#createNewMenuItem", "com.android.internal.util.XmlUtils#convertValueToInt", "com.android.internal.textservice.ITextServicesManager$Stub#asInterface", + "android.os.SystemProperties#native_get", }; /** diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 85b81d9..e73cce1 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -417,6 +417,12 @@ public class WifiConfiguration implements Parcelable { public int autoJoinStatus; /** + * Set if the configuration was self added by the framework + * @hide + */ + public boolean selfAdded; + + /** * @hide * Indicate that a WifiConfiguration is temporary and should not be saved * nor considered by AutoJoin. @@ -498,6 +504,8 @@ public class WifiConfiguration implements Parcelable { proxySettings = ProxySettings.UNASSIGNED; linkProperties = new LinkProperties(); autoJoinStatus = AUTO_JOIN_ENABLED; + selfAdded = false; + ephemeral = false; } /** @@ -794,8 +802,28 @@ public class WifiConfiguration implements Parcelable { return configKey(false); } + /** @hide + * return the config key string based on a scan result + */ + static public String configKey(ScanResult result) { + String key = "\"" + result.SSID + "\""; + + if (result.capabilities.contains("WEP")) { + key = key + "-WEP"; + } - /** Implement the Parcelable interface {@hide} */ + if (result.capabilities.contains("PSK")) { + key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK]; + } + + if (result.capabilities.contains("EAP")) { + key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP]; + } + + return key; + } + + /** Implement the Parcelable interface {@hide} */ public int describeContents() { return 0; } @@ -849,6 +877,7 @@ public class WifiConfiguration implements Parcelable { } mCachedConfigKey = null; //force null configKey autoJoinStatus = source.autoJoinStatus; + selfAdded = source.selfAdded; if (source.visibility != null) { visibility = new Visibility(source.visibility); @@ -886,6 +915,7 @@ public class WifiConfiguration implements Parcelable { dest.writeString(dhcpServer); dest.writeString(defaultGwMacAddress); dest.writeInt(autoJoinStatus); + dest.writeInt(selfAdded ? 1 : 0); } /** Implement the Parcelable interface {@hide} */ @@ -920,7 +950,7 @@ public class WifiConfiguration implements Parcelable { config.dhcpServer = in.readString(); config.defaultGwMacAddress = in.readString(); config.autoJoinStatus = in.readInt(); - + config.selfAdded = in.readInt() != 0; return config; } |
