diff options
315 files changed, 8665 insertions, 4241 deletions
@@ -76,8 +76,8 @@ LOCAL_SRC_FILES += \ core/java/android/app/ISearchManagerCallback.aidl \ core/java/android/app/IServiceConnection.aidl \ core/java/android/app/IStopUserCallback.aidl \ - core/java/android/app/task/ITaskCallback.aidl \ - core/java/android/app/task/ITaskService.aidl \ + core/java/android/app/task/ITaskCallback.aidl \ + core/java/android/app/task/ITaskService.aidl \ core/java/android/app/IThumbnailRetriever.aidl \ core/java/android/app/ITransientNotification.aidl \ core/java/android/app/IUiAutomationConnection.aidl \ @@ -199,6 +199,8 @@ LOCAL_SRC_FILES += \ core/java/android/service/dreams/IDozeHardware.aidl \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ + core/java/android/service/fingerprint/IFingerprintService.aidl \ + core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \ core/java/android/service/trust/ITrustAgentService.aidl \ core/java/android/service/trust/ITrustAgentServiceCallback.aidl \ core/java/android/service/voice/IVoiceInteractionService.aidl \ @@ -318,6 +320,7 @@ LOCAL_SRC_FILES += \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/hotspot/IWifiHotspotManager.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ + wifi/java/android/net/wifi/IWifiScanner.aidl \ packages/services/PacProcessor/com/android/net/IProxyService.aidl \ packages/services/Proxy/com/android/net/IProxyCallback.aidl \ packages/services/Proxy/com/android/net/IProxyPortListener.aidl \ @@ -718,8 +721,9 @@ include $(BUILD_DROIDDOC) $(full_target): $(framework_built) $(gen) # Run this for checkbuild -.PHONY: checkbuild checkbuild: doc-comment-check-docs +# Check comment when you are updating the API +update-api: doc-comment-check-docs # ==== static html in the sdk ================================== include $(CLEAR_VARS) diff --git a/api/current.txt b/api/current.txt index 6153842..5ca1f59 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 = 16843837; // 0x101043d + field public static final int actionBarTheme = 16843831; // 0x1010437 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 = 16843857; // 0x1010451 + field public static final int actionOverflowMenuStyle = 16843851; // 0x101044b field public static final int actionProviderClass = 16843657; // 0x1010389 field public static final int actionViewClass = 16843516; // 0x10102fc field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd @@ -295,7 +295,6 @@ package android { field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef field public static final int alwaysRetainTaskState = 16843267; // 0x1010203 field public static final int angle = 16843168; // 0x10101a0 - field public static final int animate = 16843823; // 0x101042f field public static final int animateFirstView = 16843477; // 0x10102d5 field public static final int animateLayoutChanges = 16843506; // 0x10102f2 field public static final int animateOnClick = 16843356; // 0x101025c @@ -314,6 +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 = 16843853; // 0x101044d 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,29 +382,29 @@ 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 = 16843822; // 0x101042e + field public static final int clipToPath = 16843818; // 0x101042a 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 = 16843842; // 0x1010442 + field public static final int colorAccent = 16843836; // 0x101043c 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 = 16843829; // 0x1010435 - field public static final int colorButtonNormalColored = 16843831; // 0x1010437 - field public static final int colorButtonPressed = 16843830; // 0x1010436 - field public static final int colorButtonPressedColored = 16843832; // 0x1010438 - field public static final int colorControlActivated = 16843828; // 0x1010434 - field public static final int colorControlNormal = 16843827; // 0x1010433 + field public static final int colorButtonNormal = 16843823; // 0x101042f + field public static final int colorButtonNormalColored = 16843825; // 0x1010431 + field public static final int colorButtonPressed = 16843824; // 0x1010430 + field public static final int colorButtonPressedColored = 16843826; // 0x1010432 + field public static final int colorControlActivated = 16843822; // 0x101042e + field public static final int colorControlNormal = 16843821; // 0x101042d 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 = 16843840; // 0x1010440 - field public static final int colorPrimaryDark = 16843841; // 0x1010441 - field public static final int colorPrimaryLight = 16843839; // 0x101043f + field public static final int colorPrimary = 16843834; // 0x101043a + field public static final int colorPrimaryDark = 16843835; // 0x101043b + field public static final int colorPrimaryLight = 16843833; // 0x1010439 field public static final int columnCount = 16843639; // 0x1010377 field public static final int columnDelay = 16843215; // 0x10101cf field public static final int columnOrderPreserved = 16843640; // 0x1010378 @@ -463,6 +463,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 = 16843852; // 0x101044c field public static final int drawSelectorOnTop = 16843004; // 0x10100fc field public static final int drawable = 16843161; // 0x1010199 field public static final int drawableBottom = 16843118; // 0x101016e @@ -485,14 +486,13 @@ package android { field public static final int dropDownWidth = 16843362; // 0x1010262 field public static final int duplicateParentState = 16842985; // 0x10100e9 field public static final int duration = 16843160; // 0x1010198 - field public static final int durations = 16843814; // 0x1010426 field public static final int editTextBackground = 16843602; // 0x1010352 field public static final int editTextColor = 16843601; // 0x1010351 field public static final int editTextPreferenceStyle = 16842898; // 0x1010092 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 = 16843853; // 0x101044d + field public static final int elevation = 16843847; // 0x1010447 field public static final int ellipsize = 16842923; // 0x10100ab field public static final int ems = 16843096; // 0x1010158 field public static final int enabled = 16842766; // 0x101000e @@ -502,9 +502,9 @@ 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 = 16843855; // 0x101044f + field public static final int excludeClass = 16843849; // 0x1010449 field public static final int excludeFromRecents = 16842775; // 0x1010017 - field public static final int excludeId = 16843854; // 0x101044e + field public static final int excludeId = 16843848; // 0x1010448 field public static final int exitFadeDuration = 16843533; // 0x101030d field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052 field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053 @@ -533,11 +533,11 @@ package android { 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 = 16843809; // 0x1010421 + field public static final int fill = 16843808; // 0x1010420 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 = 16843808; // 0x1010420 + field public static final int fillOpacity = 16843807; // 0x101041f field public static final int fillViewport = 16843130; // 0x101017a field public static final int filter = 16843035; // 0x101011b field public static final int filterTouchesWhenObscured = 16843460; // 0x10102c4 @@ -599,7 +599,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 = 16843856; // 0x1010450 + field public static final int hideOnContentScroll = 16843850; // 0x101044a field public static final int hint = 16843088; // 0x1010150 field public static final int homeAsUpIndicator = 16843531; // 0x101030b field public static final int homeLayout = 16843549; // 0x101031d @@ -769,7 +769,6 @@ package android { field public static final int layout_x = 16843135; // 0x101017f field public static final int layout_y = 16843136; // 0x1010180 field public static final int left = 16843181; // 0x10101ad - field public static final int limitTo = 16843824; // 0x1010430 field public static final int lineSpacingExtra = 16843287; // 0x1010217 field public static final int lineSpacingMultiplier = 16843288; // 0x1010218 field public static final int lines = 16843092; // 0x1010154 @@ -831,7 +830,7 @@ package android { field public static final int name = 16842755; // 0x1010003 field public static final int navigationMode = 16843471; // 0x10102cf field public static final int negativeButtonText = 16843254; // 0x10101f6 - field public static final int nestedScrollingEnabled = 16843843; // 0x1010443 + field public static final int nestedScrollingEnabled = 16843837; // 0x101043d field public static final int nextFocusDown = 16842980; // 0x10100e4 field public static final int nextFocusForward = 16843580; // 0x101033c field public static final int nextFocusLeft = 16842977; // 0x10100e1 @@ -873,18 +872,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 = 16843810; // 0x1010422 + field public static final int pathData = 16843809; // 0x1010421 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 = 16843833; // 0x1010439 + field public static final int persistable = 16843827; // 0x1010433 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 = 16843826; // 0x1010432 + field public static final int pinned = 16843820; // 0x101042c field public static final int pivotX = 16843189; // 0x10101b5 field public static final int pivotY = 16843190; // 0x10101b6 field public static final int popupAnimationStyle = 16843465; // 0x10102c9 @@ -939,7 +938,6 @@ package android { field public static final int readPermission = 16842759; // 0x1010007 field public static final int repeatCount = 16843199; // 0x10101bf field public static final int repeatMode = 16843200; // 0x10101c0 - field public static final int repeatStyle = 16843816; // 0x1010428 field public static final int reqFiveWayNav = 16843314; // 0x1010232 field public static final int reqHardKeyboard = 16843305; // 0x1010229 field public static final int reqKeyboardType = 16843304; // 0x1010228 @@ -949,7 +947,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 = 16843825; // 0x1010431 + field public static final int requiredForProfile = 16843819; // 0x101042b field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 field public static final int resizeMode = 16843619; // 0x1010363 @@ -1013,8 +1011,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 sequence = 16843815; // 0x1010427 - field public static final int sessionService = 16843850; // 0x101044a + field public static final int sessionService = 16843844; // 0x1010444 field public static final int settingsActivity = 16843301; // 0x1010225 field public static final int shadowColor = 16843105; // 0x1010161 field public static final int shadowDx = 16843106; // 0x1010162 @@ -1036,7 +1033,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 = 16843836; // 0x101043c + field public static final int slideEdge = 16843830; // 0x1010436 field public static final int smallIcon = 16843422; // 0x101029e field public static final int smallScreens = 16843396; // 0x1010284 field public static final int smoothScrollbar = 16843313; // 0x1010231 @@ -1053,12 +1050,13 @@ package android { 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 = 16843851; // 0x101044b + field public static final int stackViewStyle = 16843845; // 0x1010445 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 = 16843854; // 0x101044e 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 @@ -1088,13 +1086,13 @@ package android { 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 = 16843811; // 0x1010423 - field public static final int strokeLineCap = 16843820; // 0x101042c - field public static final int strokeLineJoin = 16843821; // 0x101042d - field public static final int strokeOpacity = 16843812; // 0x1010424 - field public static final int strokeWidth = 16843813; // 0x1010425 + 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 subtitle = 16843473; // 0x10102d1 - field public static final int subtitleTextAppearance = 16843835; // 0x101043b + field public static final int subtitleTextAppearance = 16843829; // 0x1010435 field public static final int subtitleTextStyle = 16843513; // 0x10102f9 field public static final int subtypeExtraValue = 16843674; // 0x101039a field public static final int subtypeId = 16843713; // 0x10103c1 @@ -1111,7 +1109,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 = 16843852; // 0x101044c + field public static final int switchStyle = 16843846; // 0x1010446 field public static final int switchTextAppearance = 16843630; // 0x101036e field public static final int switchTextOff = 16843628; // 0x101036c field public static final int switchTextOn = 16843627; // 0x101036b @@ -1148,7 +1146,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 = 16843838; // 0x101043e + field public static final int textAppearanceListItemSecondary = 16843832; // 0x1010438 field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f field public static final int textAppearanceMedium = 16842817; // 0x1010041 field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044 @@ -1212,7 +1210,7 @@ package android { field public static final int tintMode = 16843798; // 0x1010416 field public static final int title = 16843233; // 0x10101e1 field public static final int titleCondensed = 16843234; // 0x10101e2 - field public static final int titleTextAppearance = 16843834; // 0x101043a + field public static final int titleTextAppearance = 16843828; // 0x1010434 field public static final int titleTextStyle = 16843512; // 0x10102f8 field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 @@ -1237,10 +1235,9 @@ package android { 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 trigger = 16843805; // 0x101041d - field public static final int trimPathEnd = 16843818; // 0x101042a - field public static final int trimPathOffset = 16843819; // 0x101042b - field public static final int trimPathStart = 16843817; // 0x1010429 + 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 type = 16843169; // 0x10101a1 field public static final int typeface = 16842902; // 0x1010096 field public static final int uiOptions = 16843672; // 0x1010398 @@ -1265,8 +1262,8 @@ 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 viewportHeight = 16843807; // 0x101041f - field public static final int viewportWidth = 16843806; // 0x101041e + field public static final int viewportHeight = 16843806; // 0x101041e + field public static final int viewportWidth = 16843805; // 0x101041d field public static final int visibility = 16842972; // 0x10100dc field public static final int visible = 16843156; // 0x1010194 field public static final int vmSafeMode = 16843448; // 0x10102b8 @@ -1295,8 +1292,8 @@ package android { field public static final int windowActionBar = 16843469; // 0x10102cd field public static final int windowActionBarOverlay = 16843492; // 0x10102e4 field public static final int windowActionModeOverlay = 16843485; // 0x10102dd - field public static final int windowAllowEnterTransitionOverlap = 16843849; // 0x1010449 - field public static final int windowAllowExitTransitionOverlap = 16843848; // 0x1010448 + field public static final int windowAllowEnterTransitionOverlap = 16843843; // 0x1010443 + field public static final int windowAllowExitTransitionOverlap = 16843842; // 0x1010442 field public static final int windowAnimationStyle = 16842926; // 0x10100ae field public static final int windowBackground = 16842836; // 0x1010054 field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b @@ -1306,9 +1303,9 @@ package android { field public static final int windowDisablePreview = 16843298; // 0x1010222 field public static final int windowEnableSplitTouch = 16843543; // 0x1010317 field public static final int windowEnterAnimation = 16842932; // 0x10100b4 - field public static final int windowEnterTransition = 16843844; // 0x1010444 + field public static final int windowEnterTransition = 16843838; // 0x101043e field public static final int windowExitAnimation = 16842933; // 0x10100b5 - field public static final int windowExitTransition = 16843845; // 0x1010445 + field public static final int windowExitTransition = 16843839; // 0x101043f field public static final int windowFrame = 16842837; // 0x1010055 field public static final int windowFullscreen = 16843277; // 0x101020d field public static final int windowHideAnimation = 16842935; // 0x10100b7 @@ -1319,8 +1316,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 = 16843846; // 0x1010446 - field public static final int windowSharedElementExitTransition = 16843847; // 0x1010447 + field public static final int windowSharedElementEnterTransition = 16843840; // 0x1010440 + field public static final int windowSharedElementExitTransition = 16843841; // 0x1010441 field public static final int windowShowAnimation = 16842934; // 0x10100b6 field public static final int windowShowWallpaper = 16843410; // 0x1010292 field public static final int windowSoftInputMode = 16843307; // 0x101022b @@ -2786,6 +2783,7 @@ package android.animation { public class AnimatorInflater { ctor public AnimatorInflater(); method public static android.animation.Animator loadAnimator(android.content.Context, int) throws android.content.res.Resources.NotFoundException; + method public static android.animation.StateListAnimator loadStateListAnimator(android.content.Context, int) throws android.content.res.Resources.NotFoundException; } public abstract class AnimatorListenerAdapter implements android.animation.Animator.AnimatorListener android.animation.Animator.AnimatorPauseListener { @@ -2982,6 +2980,12 @@ package android.animation { method public android.graphics.Rect evaluate(float, android.graphics.Rect, android.graphics.Rect); } + public class StateListAnimator { + ctor public StateListAnimator(); + method public void addState(int[], android.animation.Animator); + method public void jumpToCurrentState(); + } + public class TimeAnimator extends android.animation.ValueAnimator { ctor public TimeAnimator(); method public void setTimeListener(android.animation.TimeAnimator.TimeListener); @@ -3361,10 +3365,12 @@ package android.app { method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; method public void startIntentSenderFromChild(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSenderFromChild(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; + method public void startLockTask(); method public deprecated void startManagingCursor(android.database.Cursor); method public boolean startNextMatchingActivity(android.content.Intent); method public boolean startNextMatchingActivity(android.content.Intent, android.os.Bundle); method public void startSearch(java.lang.String, boolean, android.os.Bundle, boolean); + method public void stopLockTask(); method public deprecated void stopManagingCursor(android.database.Cursor); method public void takeKeyEvents(boolean); method public void triggerSearch(java.lang.String, android.os.Bundle); @@ -4426,6 +4432,7 @@ package android.app { field public static final java.lang.String CATEGORY_STATUS = "status"; field public static final java.lang.String CATEGORY_SYSTEM = "sys"; field public static final java.lang.String CATEGORY_TRANSPORT = "transport"; + field public static final int COLOR_DEFAULT = 0; // 0x0 field public static final android.os.Parcelable.Creator CREATOR; field public static final int DEFAULT_ALL = -1; // 0xffffffff field public static final int DEFAULT_LIGHTS = 4; // 0x4 @@ -4471,6 +4478,7 @@ package android.app { field public int audioStreamType; field public android.widget.RemoteViews bigContentView; field public java.lang.String category; + field public int color; field public android.app.PendingIntent contentIntent; field public android.widget.RemoteViews contentView; field public int defaults; @@ -4533,6 +4541,7 @@ package android.app { method public deprecated android.app.Notification getNotification(); method public android.app.Notification.Builder setAutoCancel(boolean); method public android.app.Notification.Builder setCategory(java.lang.String); + method public android.app.Notification.Builder setColor(int); method public android.app.Notification.Builder setContent(android.widget.RemoteViews); method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence); method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent); @@ -5013,6 +5022,9 @@ package android.app.admin { method public void clearForwardingIntentFilters(android.content.ComponentName); method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String); method public void clearUserRestriction(android.content.ComponentName, java.lang.String); + method public void enableSystemApp(android.content.ComponentName, java.lang.String); + method public int enableSystemApp(android.content.ComponentName, android.content.Intent); + method public java.lang.String[] getAccountTypesWithManagementDisabled(); method public java.util.List<android.content.ComponentName> getActiveAdmins(); method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String); method public boolean getCameraDisabled(android.content.ComponentName); @@ -5038,13 +5050,16 @@ package android.app.admin { method public boolean isActivePasswordSufficient(); method public boolean isAdminActive(android.content.ComponentName); method public boolean isDeviceOwnerApp(java.lang.String); + method public boolean isLockTaskPermitted(android.content.ComponentName); method public boolean isProfileOwnerApp(java.lang.String); method public void lockNow(); method public void removeActiveAdmin(android.content.ComponentName); method public boolean resetPassword(java.lang.String, int); + method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean); method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void setCameraDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); + method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException; method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int); method public void setMaximumTimeToLock(android.content.ComponentName, long); method public void setPasswordExpirationTimeout(android.content.ComponentName, long); @@ -6614,6 +6629,7 @@ package android.content { field public static final java.lang.String DISPLAY_SERVICE = "display"; field public static final java.lang.String DOWNLOAD_SERVICE = "download"; field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; + field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint"; field public static final java.lang.String HDMI_CEC_SERVICE = "hdmi_cec"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; @@ -7149,6 +7165,7 @@ package android.content { field public static final int FILL_IN_PACKAGE = 16; // 0x10 field public static final int FILL_IN_SELECTOR = 64; // 0x40 field public static final int FILL_IN_SOURCE_BOUNDS = 32; // 0x20 + field public static final int FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 field public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304; // 0x400000 field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000 field public static final int FLAG_ACTIVITY_CLEAR_TOP = 67108864; // 0x4000000 @@ -7667,8 +7684,12 @@ package android.content.pm { field public static final int CONFIG_TOUCHSCREEN = 8; // 0x8 field public static final int CONFIG_UI_MODE = 512; // 0x200 field public static final android.os.Parcelable.Creator CREATOR; + field public static final int DOCUMENT_LAUNCH_ALWAYS = 2; // 0x2 + field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1 + field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0 field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40 field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8 + field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4 field public static final int FLAG_EXCLUDE_FROM_RECENTS = 32; // 0x20 field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100 @@ -7677,6 +7698,7 @@ package android.content.pm { field public static final int FLAG_IMMERSIVE = 2048; // 0x800 field public static final int FLAG_MULTIPROCESS = 1; // 0x1 field public static final int FLAG_NO_HISTORY = 128; // 0x80 + field public static final int FLAG_PERSISTABLE = 4096; // 0x1000 field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000 field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10 field public static final int LAUNCH_MULTIPLE = 0; // 0x0 @@ -7701,6 +7723,7 @@ package android.content.pm { field public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12; // 0xc field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1 field public int configChanges; + field public int documentLaunchMode; field public int flags; field public int launchMode; field public java.lang.String parentActivityName; @@ -8026,6 +8049,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera"; field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any"; field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus"; + field public static final java.lang.String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external"; field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash"; field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front"; field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir"; @@ -8051,6 +8075,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer"; field public static final java.lang.String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass"; field public static final java.lang.String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope"; + field public static final java.lang.String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate"; field public static final java.lang.String FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light"; field public static final java.lang.String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity"; field public static final java.lang.String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter"; @@ -10734,6 +10759,7 @@ package android.graphics { method public void releaseTexImage(); method public void setDefaultBufferSize(int, int); method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener); + method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener, android.os.Handler); method public void updateTexImage(); } @@ -11838,7 +11864,6 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_WHITE_LEVEL; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_MAX_ANALOG_SENSITIVITY; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_ORIENTATION; - field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT1; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT2; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES; @@ -12220,8 +12245,6 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_GREEN_SPLIT; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_NEUTRAL_COLOR_POINT; - field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_HUE_SAT_MAP; - field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_TONE_CURVE; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA; @@ -13558,6 +13581,7 @@ package android.media { method public void stop() throws java.lang.IllegalStateException; method public int write(byte[], int, int); method public int write(short[], int, int); + method public int write(java.nio.ByteBuffer, int, int); field public static final int ERROR = -1; // 0xffffffff field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe field public static final int ERROR_INVALID_OPERATION = -3; // 0xfffffffd @@ -13570,6 +13594,8 @@ package android.media { field public static final int STATE_NO_STATIC_DATA = 2; // 0x2 field public static final int STATE_UNINITIALIZED = 0; // 0x0 field public static final int SUCCESS = 0; // 0x0 + field public static final int WRITE_BLOCKING = 0; // 0x0 + field public static final int WRITE_NON_BLOCKING = 1; // 0x1 } public static abstract interface AudioTrack.OnPlaybackPositionUpdateListener { @@ -15462,12 +15488,15 @@ package android.media.session { method public void disconnect(android.media.session.RouteInfo); method public android.media.session.SessionToken getSessionToken(); method public android.media.session.TransportPerformer getTransportPerformer(); - method public void publish(); + method public boolean isActive(); method public void release(); method public void removeCallback(android.media.session.Session.Callback); method public void sendEvent(java.lang.String, android.os.Bundle); + method public void setActive(boolean); + method public void setFlags(int); method public void setRouteOptions(java.util.List<android.media.session.RouteOptions>); - method public android.media.session.TransportPerformer setTransportPerformerEnabled(); + field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1 + field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2 } public static abstract class Session.Callback { @@ -15506,7 +15535,7 @@ package android.media.session { public final class SessionManager { method public android.media.session.Session createSession(java.lang.String); - method public java.util.List<android.media.session.SessionController> getActiveSessions(); + method public java.util.List<android.media.session.SessionController> getActiveSessions(android.content.ComponentName); } public class SessionToken implements android.os.Parcelable { @@ -15696,6 +15725,7 @@ package android.net { method public android.net.NetworkInfo getActiveNetworkInfo(); method public android.net.NetworkInfo[] getAllNetworkInfo(); method public deprecated boolean getBackgroundDataSetting(); + method public android.net.ProxyInfo getGlobalProxy(); method public android.net.NetworkInfo getNetworkInfo(int); method public int getNetworkPreference(); method public boolean isActiveNetworkMetered(); @@ -15703,6 +15733,7 @@ package android.net { method public static boolean isNetworkTypeValid(int); method public void registerNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener); method public boolean requestRouteToHost(int, int); + method public void setGlobalProxy(android.net.ProxyInfo); method public void setNetworkPreference(int); method public int startUsingNetworkFeature(int, java.lang.String); method public int stopUsingNetworkFeature(int, java.lang.String); @@ -15878,9 +15909,22 @@ package android.net { method public static final deprecated int getDefaultPort(); method public static final deprecated java.lang.String getHost(android.content.Context); method public static final deprecated int getPort(android.content.Context); + field public static final java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE"; } + public class ProxyInfo implements android.os.Parcelable { + method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int); + method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int, java.util.List<java.lang.String>); + method public static android.net.ProxyInfo buildPacProxy(android.net.Uri); + method public int describeContents(); + method public java.lang.String[] getExclusionList(); + method public java.lang.String getHost(); + method public android.net.Uri getPacFileUrl(); + method public int getPort(); + method public void writeToParcel(android.os.Parcel, int); + } + public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory { ctor public deprecated SSLCertificateSocketFactory(int); method public java.net.Socket createSocket(java.net.Socket, java.lang.String, int, boolean) throws java.io.IOException; @@ -20504,16 +20548,28 @@ package android.os { method public void setUserRestriction(java.lang.String, boolean); method public void setUserRestrictions(android.os.Bundle); method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle); + field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user"; + field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume"; + field public static final java.lang.String DISALLOW_CONFIG_APPS = "no_config_apps"; field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; + field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts"; field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials"; + field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks"; + field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering"; + field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn"; field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi"; + field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features"; + field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset"; field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps"; field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources"; field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts"; + field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media"; field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user"; field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; + field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone"; field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer"; + field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps"; } public abstract class Vibrator { @@ -24536,6 +24592,7 @@ package android.renderscript { method public void contextDump(); method public static android.renderscript.RenderScript create(android.content.Context); method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType); + method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, long); method public void destroy(); method public void finish(); method public final android.content.Context getApplicationContext(); @@ -24545,6 +24602,9 @@ package android.renderscript { method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler); method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler); method public void setPriority(android.renderscript.RenderScript.Priority); + field public static final long CREATE_FLAG_LOW_LATENCY = 1L; // 0x1L + field public static final long CREATE_FLAG_LOW_POWER = 2L; // 0x2L + field public static final long CREATE_FLAG_NONE = 0L; // 0x0L } public static final class RenderScript.ContextType extends java.lang.Enum { @@ -25058,6 +25118,36 @@ package android.service.dreams { } +package android.service.fingerprint { + + public class FingerprintManager { + ctor public FingerprintManager(android.content.Context); + method public void enroll(long); + method public void remove(int); + method public void startListening(android.service.fingerprint.FingerprintManagerReceiver); + method public void stopListening(); + field protected static final boolean DEBUG = true; + field public static final int FINGERPRINT_ERROR = -1; // 0xffffffff + field public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; // 0x2 + field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1 + field public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; // 0xfffffff6 + field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4 + field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3 + field public static final int FINGERPRINT_SCANNED = 1; // 0x1 + field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; // 0x2 + field public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; // 0x4 + } + + public class FingerprintManagerReceiver { + ctor public FingerprintManagerReceiver(); + method public void onEnrollResult(int, int); + method public void onError(int); + method public void onRemoved(int); + method public void onScanned(int, int); + } + +} + package android.service.notification { public abstract class NotificationListenerService extends android.app.Service { @@ -30475,6 +30565,7 @@ package android.view { method public final int getScrollY(); method public java.lang.String getSharedElementName(); method public int getSolidColor(); + method public android.animation.StateListAnimator getStateListAnimator(); method protected int getSuggestedMinimumHeight(); method protected int getSuggestedMinimumWidth(); method public int getSystemUiVisibility(); @@ -30739,6 +30830,7 @@ package android.view { method public void setSelected(boolean); method public void setSharedElementName(java.lang.String); method public void setSoundEffectsEnabled(boolean); + method public void setStateListAnimator(android.animation.StateListAnimator); method public void setSystemUiVisibility(int); method public void setTag(java.lang.Object); method public void setTag(int, java.lang.Object); @@ -31595,26 +31687,17 @@ package android.view { method public abstract void onFocusLost(android.view.WindowId); } - public class WindowInsets { + public final class WindowInsets { ctor public WindowInsets(android.view.WindowInsets); - method public android.view.WindowInsets cloneWithSystemWindowInsets(int, int, int, int); - method public android.view.WindowInsets cloneWithSystemWindowInsetsConsumed(); - method public android.view.WindowInsets cloneWithSystemWindowInsetsConsumed(boolean, boolean, boolean, boolean); - method public android.view.WindowInsets cloneWithWindowDecorInsets(int, int, int, int); - method public android.view.WindowInsets cloneWithWindowDecorInsetsConsumed(); - method public android.view.WindowInsets cloneWithWindowDecorInsetsConsumed(boolean, boolean, boolean, boolean); + method public android.view.WindowInsets consumeSystemWindowInsets(); method public int getSystemWindowInsetBottom(); method public int getSystemWindowInsetLeft(); method public int getSystemWindowInsetRight(); method public int getSystemWindowInsetTop(); - method public int getWindowDecorInsetBottom(); - method public int getWindowDecorInsetLeft(); - method public int getWindowDecorInsetRight(); - method public int getWindowDecorInsetTop(); method public boolean hasInsets(); method public boolean hasSystemWindowInsets(); - method public boolean hasWindowDecorInsets(); method public boolean isRound(); + method public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int); } public abstract interface WindowManager implements android.view.ViewManager { @@ -32941,6 +33024,16 @@ package android.webkit { method public boolean hasMimeType(java.lang.String); } + public abstract interface PermissionRequest { + method public abstract void deny(); + method public abstract android.net.Uri getOrigin(); + method public abstract long getResources(); + method public abstract void grant(long); + field public static final long RESOURCE_AUDIO_CAPTURE = 4L; // 0x4L + field public static final long RESOURCE_GEOLOCATION = 1L; // 0x1L + field public static final long RESOURCE_VIDEO_CAPTURE = 2L; // 0x2L + } + public abstract interface PluginStub { method public abstract android.view.View getEmbeddedView(int, android.content.Context); method public abstract android.view.View getFullScreenView(int, android.content.Context); @@ -33000,6 +33093,8 @@ package android.webkit { method public boolean onJsConfirm(android.webkit.WebView, java.lang.String, java.lang.String, android.webkit.JsResult); method public boolean onJsPrompt(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String, android.webkit.JsPromptResult); method public deprecated boolean onJsTimeout(); + method public void onPermissionRequest(android.webkit.PermissionRequest); + method public void onPermissionRequestCanceled(android.webkit.PermissionRequest); method public void onProgressChanged(android.webkit.WebView, int); method public deprecated void onReachedMaxAppCacheSize(long, long, android.webkit.WebStorage.QuotaUpdater); method public void onReceivedIcon(android.webkit.WebView, android.graphics.Bitmap); @@ -33287,6 +33382,7 @@ package android.webkit { method public boolean pageUp(boolean); method public void pauseTimers(); method public void postUrl(java.lang.String, byte[]); + method public void preauthorizePermission(android.net.Uri, long); method public void reload(); method public void removeJavascriptInterface(java.lang.String); method public void requestFocusNodeHref(android.os.Message); @@ -39739,11 +39835,13 @@ package java.net { method public java.lang.String getValue(); method public int getVersion(); method public boolean hasExpired(); + method public boolean isHttpOnly(); method public static java.util.List<java.net.HttpCookie> parse(java.lang.String); method public void setComment(java.lang.String); method public void setCommentURL(java.lang.String); method public void setDiscard(boolean); method public void setDomain(java.lang.String); + method public void setHttpOnly(boolean); method public void setMaxAge(long); method public void setPath(java.lang.String); method public void setPortlist(java.lang.String); @@ -40696,7 +40794,7 @@ package java.nio.channels { ctor public ConnectionPendingException(); } - public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel { + public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel { ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider); method public java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException; method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException; @@ -40704,6 +40802,8 @@ package java.nio.channels { method public java.net.SocketAddress getLocalAddress() throws java.io.IOException; method public T getOption(java.net.SocketOption<T>) throws java.io.IOException; method public abstract boolean isConnected(); + method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException; + method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException; method public static java.nio.channels.DatagramChannel open() throws java.io.IOException; method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException; method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException; @@ -40751,6 +40851,7 @@ package java.nio.channels { public abstract class FileLock implements java.lang.AutoCloseable { ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean); + method public java.nio.channels.Channel acquiredBy(); method public final java.nio.channels.FileChannel channel(); method public final void close() throws java.io.IOException; method public final boolean isShared(); @@ -40783,6 +40884,24 @@ package java.nio.channels { method public abstract void close() throws java.io.IOException; } + public abstract class MembershipKey { + ctor protected MembershipKey(); + method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException; + method public abstract java.nio.channels.MulticastChannel channel(); + method public abstract void drop(); + method public abstract java.net.InetAddress group(); + method public abstract boolean isValid(); + method public abstract java.net.NetworkInterface networkInterface(); + method public abstract java.net.InetAddress sourceAddress(); + method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress); + } + + public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel { + method public abstract void close() throws java.io.IOException; + method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException; + method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException; + } + public abstract interface NetworkChannel implements java.lang.AutoCloseable java.nio.channels.Channel java.io.Closeable { method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException; method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException; @@ -44888,6 +45007,7 @@ package java.util { method public java.lang.String getDisplayName(java.util.Locale); method public static java.util.Currency getInstance(java.lang.String); method public static java.util.Currency getInstance(java.util.Locale); + method public int getNumericCode(); method public java.lang.String getSymbol(); method public java.lang.String getSymbol(java.util.Locale); } @@ -47970,8 +48090,10 @@ package java.util.zip { public class ZipFile implements java.io.Closeable { ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException; + ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException, java.util.zip.ZipException; ctor public ZipFile(java.lang.String) throws java.io.IOException; ctor public ZipFile(java.io.File, int) throws java.io.IOException; + ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException; method public void close() throws java.io.IOException; method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries(); method public java.lang.String getComment(); @@ -48025,6 +48147,7 @@ package java.util.zip { public class ZipInputStream extends java.util.zip.InflaterInputStream { ctor public ZipInputStream(java.io.InputStream); + ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset); method public void closeEntry() throws java.io.IOException; method protected java.util.zip.ZipEntry createZipEntry(java.lang.String); method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException; @@ -48072,6 +48195,7 @@ package java.util.zip { public class ZipOutputStream extends java.util.zip.DeflaterOutputStream { ctor public ZipOutputStream(java.io.OutputStream); + ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset); method public void closeEntry() throws java.io.IOException; method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException; method public void setComment(java.lang.String); diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index 20236aa..933135d 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -21,6 +21,7 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.content.res.Resources.NotFoundException; import android.util.AttributeSet; +import android.util.StateSet; import android.util.TypedValue; import android.util.Xml; import android.view.animation.AnimationUtils; @@ -87,9 +88,86 @@ public class AnimatorInflater { } } + public static StateListAnimator loadStateListAnimator(Context context, int id) + throws NotFoundException { + XmlResourceParser parser = null; + try { + parser = context.getResources().getAnimation(id); + return createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); + } catch (XmlPullParserException ex) { + Resources.NotFoundException rnf = + new Resources.NotFoundException( + "Can't load state list animator resource ID #0x" + + Integer.toHexString(id) + ); + rnf.initCause(ex); + throw rnf; + } catch (IOException ex) { + Resources.NotFoundException rnf = + new Resources.NotFoundException( + "Can't load state list animator resource ID #0x" + + Integer.toHexString(id) + ); + rnf.initCause(ex); + throw rnf; + } finally { + if (parser != null) { + parser.close(); + } + } + } + + private static StateListAnimator createStateListAnimatorFromXml(Context context, + XmlPullParser parser, AttributeSet attributeSet) + throws IOException, XmlPullParserException { + int type; + StateListAnimator stateListAnimator = new StateListAnimator(); + + while (true) { + type = parser.next(); + switch (type) { + case XmlPullParser.END_DOCUMENT: + case XmlPullParser.END_TAG: + return stateListAnimator; + + case XmlPullParser.START_TAG: + // parse item + Animator animator = null; + if ("item".equals(parser.getName())) { + int attributeCount = parser.getAttributeCount(); + int[] states = new int[attributeCount]; + int stateIndex = 0; + for (int i = 0; i < attributeCount; i++) { + int attrName = attributeSet.getAttributeNameResource(i); + if (attrName == com.android.internal.R.attr.animation) { + animator = loadAnimator(context, + attributeSet.getAttributeResourceValue(i, 0)); + } else { + states[stateIndex++] = + attributeSet.getAttributeBooleanValue(i, false) ? + attrName : -attrName; + } + + } + if (animator == null) { + animator = createAnimatorFromXml(context, parser); + } + + if (animator == null) { + throw new Resources.NotFoundException( + "animation state item must have a valid animation"); + } + stateListAnimator + .addState(StateSet.trimStateSet(states, stateIndex), animator); + + } + break; + } + } + } + private static Animator createAnimatorFromXml(Context c, XmlPullParser parser) throws XmlPullParserException, IOException { - return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0); } diff --git a/core/java/android/animation/StateListAnimator.java b/core/java/android/animation/StateListAnimator.java new file mode 100644 index 0000000..bc4843d --- /dev/null +++ b/core/java/android/animation/StateListAnimator.java @@ -0,0 +1,209 @@ +/* + * 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.animation; + +import android.util.StateSet; +import android.view.View; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * Lets you define a number of Animators that will run on the attached View depending on the View's + * drawable state. + * <p> + * It can be defined in an XML file with the <code><selector></code> element. + * Each State Animator is defined in a nested <code><item></code> element. + * + * @attr ref android.R.styleable#DrawableStates_state_focused + * @attr ref android.R.styleable#DrawableStates_state_window_focused + * @attr ref android.R.styleable#DrawableStates_state_enabled + * @attr ref android.R.styleable#DrawableStates_state_checkable + * @attr ref android.R.styleable#DrawableStates_state_checked + * @attr ref android.R.styleable#DrawableStates_state_selected + * @attr ref android.R.styleable#DrawableStates_state_activated + * @attr ref android.R.styleable#DrawableStates_state_active + * @attr ref android.R.styleable#DrawableStates_state_single + * @attr ref android.R.styleable#DrawableStates_state_first + * @attr ref android.R.styleable#DrawableStates_state_middle + * @attr ref android.R.styleable#DrawableStates_state_last + * @attr ref android.R.styleable#DrawableStates_state_pressed + * @attr ref android.R.styleable#StateListAnimatorItem_animation + */ +public class StateListAnimator { + + private final ArrayList<Tuple> mTuples = new ArrayList<Tuple>(); + + private Tuple mLastMatch = null; + + private Animator mRunningAnimator = null; + + private WeakReference<View> mViewRef; + + private AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (mRunningAnimator == animation) { + mRunningAnimator = null; + } + } + }; + + /** + * Associates the given animator with the provided drawable state specs so that it will be run + * when the View's drawable state matches the specs. + * + * @param specs The drawable state specs to match against + * @param animator The animator to run when the specs match + */ + public void addState(int[] specs, Animator animator) { + Tuple tuple = new Tuple(specs, animator); + tuple.mAnimator.addListener(mAnimatorListener); + mTuples.add(tuple); + } + + /** + * Returns the current {@link android.animation.Animator} which is started because of a state + * change. + * + * @return The currently running Animator or null if no Animator is running + * @hide + */ + public Animator getRunningAnimator() { + return mRunningAnimator; + } + + /** + * @hide + */ + public View getTarget() { + return mViewRef == null ? null : mViewRef.get(); + } + + /** + * Called by View + * @hide + */ + public void setTarget(View view) { + final View current = getTarget(); + if (current == view) { + return; + } + if (current != null) { + clearTarget(); + } + if (view != null) { + mViewRef = new WeakReference<View>(view); + } + + } + + private void clearTarget() { + final int size = mTuples.size(); + for (int i = 0; i < size; i++) { + mTuples.get(i).mAnimator.setTarget(null); + } + + mViewRef = null; + mLastMatch = null; + mRunningAnimator = null; + } + + /** + * Called by View + * @hide + */ + public void setState(int[] state) { + Tuple match = null; + final int count = mTuples.size(); + for (int i = 0; i < count; i++) { + final Tuple tuple = mTuples.get(i); + if (StateSet.stateSetMatches(tuple.mSpecs, state)) { + match = tuple; + break; + } + } + if (match == mLastMatch) { + return; + } + if (mLastMatch != null) { + cancel(mLastMatch); + } + mLastMatch = match; + if (match != null) { + start(match); + } + } + + private void start(Tuple match) { + match.mAnimator.setTarget(getTarget()); + mRunningAnimator = match.mAnimator; + match.mAnimator.start(); + } + + private void cancel(Tuple lastMatch) { + lastMatch.mAnimator.cancel(); + lastMatch.mAnimator.setTarget(null); + } + + /** + * @hide + */ + public ArrayList<Tuple> getTuples() { + return mTuples; + } + + /** + * If there is an animation running for a recent state change, ends it. + * <p> + * This causes the animation to assign the end value(s) to the View. + */ + public void jumpToCurrentState() { + if (mRunningAnimator != null) { + mRunningAnimator.end(); + } + } + + /** + * @hide + */ + public static class Tuple { + + final int[] mSpecs; + + final Animator mAnimator; + + private Tuple(int[] specs, Animator animator) { + mSpecs = specs; + mAnimator = animator; + } + + /** + * @hide + */ + public int[] getSpecs() { + return mSpecs; + } + + /** + * @hide + */ + public Animator getAnimator() { + return mAnimator; + } + } +} diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 08caa14..ef6fcb7 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -29,6 +29,7 @@ import com.android.internal.policy.PolicyManager; import android.annotation.IntDef; import android.annotation.Nullable; +import android.app.admin.DevicePolicyManager; import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.ContentResolver; @@ -5690,7 +5691,16 @@ public class Activity extends ContextThemeWrapper } } - /** @hide */ + /** + * Put this Activity in a mode where the user is locked to the + * current task. + * + * This will prevent the user from launching other apps, going to settings, + * or reaching the home screen. + * + * Lock task mode will only start if the activity has been whitelisted by the + * Device Owner through DevicePolicyManager#setLockTaskComponents. + */ public void startLockTask() { try { ActivityManagerNative.getDefault().startLockTaskMode(mToken); @@ -5698,7 +5708,15 @@ public class Activity extends ContextThemeWrapper } } - /** @hide */ + /** + * Allow the user to switch away from the current task. + * + * Called to end the mode started by {@link Activity#startLockTask}. This + * can only be called by activities that have successfully called + * startLockTask previously. + * + * This will allow the user to exit this app and move onto other activities. + */ public void stopLockTask() { try { ActivityManagerNative.getDefault().stopLockTaskMode(); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5d809d8..044727d 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -20,7 +20,6 @@ import android.os.BatteryStats; import android.os.IBinder; import com.android.internal.app.IUsageStats; import com.android.internal.app.ProcessStats; -import com.android.internal.os.PkgUsageStats; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; @@ -2130,14 +2129,15 @@ public class ActivityManager { return new HashMap<String, Integer>(); } - PkgUsageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats(); + UsageStats.PackageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats( + ActivityThread.currentPackageName()); if (allPkgUsageStats == null) { return new HashMap<String, Integer>(); } Map<String, Integer> launchCounts = new HashMap<String, Integer>(); - for (PkgUsageStats pkgUsageStats : allPkgUsageStats) { - launchCounts.put(pkgUsageStats.packageName, pkgUsageStats.launchCount); + for (UsageStats.PackageStats pkgUsageStats : allPkgUsageStats) { + launchCounts.put(pkgUsageStats.getPackageName(), pkgUsageStats.getLaunchCount()); } return launchCounts; @@ -2251,17 +2251,17 @@ public class ActivityManager { * * @hide */ - public PkgUsageStats[] getAllPackageUsageStats() { + public UsageStats.PackageStats[] getAllPackageUsageStats() { try { IUsageStats usageStatsService = IUsageStats.Stub.asInterface( ServiceManager.getService("usagestats")); if (usageStatsService != null) { - return usageStatsService.getAllPkgUsageStats(); + return usageStatsService.getAllPkgUsageStats(ActivityThread.currentPackageName()); } } catch (RemoteException e) { Log.w(TAG, "Could not query usage stats", e); } - return new PkgUsageStats[0]; + return new UsageStats.PackageStats[0]; } /** diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3b2ff7f..b606088 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -46,7 +46,7 @@ import android.graphics.Canvas; import android.hardware.display.DisplayManagerGlobal; import android.net.IConnectivityManager; import android.net.Proxy; -import android.net.ProxyProperties; +import android.net.ProxyInfo; import android.opengl.GLUtils; import android.os.AsyncTask; import android.os.Binder; @@ -4294,8 +4294,8 @@ public final class ActivityThread { // crash if we can't get it. IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); try { - ProxyProperties proxyProperties = service.getProxy(); - Proxy.setHttpProxySystemProperty(proxyProperties); + ProxyInfo proxyInfo = service.getProxy(); + Proxy.setHttpProxySystemProperty(proxyInfo); } catch (RemoteException e) {} } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index b616c1e..d813dab 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -16,6 +16,7 @@ package android.app; +import android.Manifest; import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; @@ -184,8 +185,10 @@ public class AppOpsManager { public static final int OP_MONITOR_LOCATION = 41; /** @hide Continually monitoring location data with a relatively high power request. */ public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42; + /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */ + public static final int OP_GET_USAGE_STATS = 43; /** @hide */ - public static final int _NUM_OP = 43; + public static final int _NUM_OP = 44; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = @@ -252,6 +255,7 @@ public class AppOpsManager { OP_WAKE_LOCK, OP_COARSE_LOCATION, OP_COARSE_LOCATION, + OP_GET_USAGE_STATS, }; /** @@ -302,6 +306,7 @@ public class AppOpsManager { null, OPSTR_MONITOR_LOCATION, OPSTR_MONITOR_HIGH_POWER_LOCATION, + null, }; /** @@ -352,6 +357,7 @@ public class AppOpsManager { "WAKE_LOCK", "MONITOR_LOCATION", "MONITOR_HIGH_POWER_LOCATION", + "GET_USAGE_STATS" }; /** @@ -402,6 +408,7 @@ public class AppOpsManager { android.Manifest.permission.WAKE_LOCK, null, // no permission for generic location monitoring null, // no permission for high power location monitoring + android.Manifest.permission.PACKAGE_USAGE_STATS, }; /** @@ -451,6 +458,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS }; /** @@ -504,6 +512,7 @@ public class AppOpsManager { false, false, false, + false, }; private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index fe532bf..801182d 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -16,6 +16,8 @@ package android.app; +import android.net.wifi.IWifiScanner; +import android.net.wifi.WifiScanner; import android.os.Build; import com.android.internal.policy.PolicyManager; @@ -106,6 +108,9 @@ import android.os.storage.IMountService; import android.os.storage.StorageManager; import android.print.IPrintManager; import android.print.PrintManager; +import android.service.fingerprint.FingerprintManager; +import android.service.fingerprint.FingerprintManagerReceiver; +import android.service.fingerprint.FingerprintService; import android.telephony.TelephonyManager; import android.tv.ITvInputManager; import android.tv.TvInputManager; @@ -451,6 +456,11 @@ class ContextImpl extends Context { return new KeyguardManager(); }}); + registerService(FINGERPRINT_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new FingerprintManager(ctx); + }}); + registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); @@ -581,6 +591,13 @@ class ContextImpl extends Context { return new WifiP2pManager(service); }}); + registerService(WIFI_SCANNING_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(WIFI_SCANNING_SERVICE); + IWifiScanner service = IWifiScanner.Stub.asInterface(b); + return new WifiScanner(ctx.getOuterContext(), service); + }}); + registerService(WINDOW_SERVICE, new ServiceFetcher() { Display mDefaultDisplay; public Object getService(ContextImpl ctx) { @@ -661,6 +678,11 @@ class ContextImpl extends Context { return new NetworkScoreManager(ctx); } }); + + registerService(USAGE_STATS_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new UsageStatsManager(ctx.getOuterContext()); + }}); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 25a1493..bba6caf 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -17,7 +17,7 @@ package android.app; import com.android.internal.R; -import com.android.internal.util.LegacyNotificationUtil; +import com.android.internal.util.NotificationColorUtil; import android.annotation.IntDef; import android.content.Context; @@ -28,6 +28,7 @@ import android.graphics.PorterDuff; import android.media.AudioManager; import android.net.Uri; import android.os.BadParcelableException; +import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -420,6 +421,21 @@ public class Notification implements Parcelable @Priority public int priority; + /** + * Accent color (an ARGB integer like the constants in {@link android.graphics.Color}) + * to be applied by the standard Style templates when presenting this notification. + * + * The current template design constructs a colorful header image by overlaying the + * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are + * ignored. + */ + public int color = COLOR_DEFAULT; + + /** + * Special value of {@link #color} telling the system not to decorate this notification with + * any special color but instead use default colors when presenting this notification. + */ + public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT /** * Sphere of visibility of this notification, which affects how and when the SystemUI reveals @@ -877,6 +893,8 @@ public class Notification implements Parcelable if (parcel.readInt() != 0) { publicVersion = Notification.CREATOR.createFromParcel(parcel); } + + color = parcel.readInt(); } @Override @@ -968,6 +986,8 @@ public class Notification implements Parcelable this.publicVersion.cloneInto(that.publicVersion, heavy); } + that.color = this.color; + if (!heavy) { that.lightenPayload(); // will clean out extras } @@ -1110,6 +1130,8 @@ public class Notification implements Parcelable } else { parcel.writeInt(0); } + + parcel.writeInt(color); } /** @@ -1218,6 +1240,7 @@ public class Notification implements Parcelable sb.append(Integer.toHexString(this.defaults)); sb.append(" flags=0x"); sb.append(Integer.toHexString(this.flags)); + sb.append(String.format(" color=0x%08x", this.color)); sb.append(" category="); sb.append(this.category); if (actions != null) { sb.append(" "); @@ -1309,9 +1332,10 @@ public class Notification implements Parcelable private boolean mShowWhen = true; private int mVisibility = VISIBILITY_PRIVATE; private Notification mPublicVersion = null; - private boolean mQuantumTheme; - private final LegacyNotificationUtil mLegacyNotificationUtil; + private final NotificationColorUtil mColorUtil; private ArrayList<String> mPeople; + private boolean mPreQuantum; + private int mColor = COLOR_DEFAULT; /** * Constructs a new Builder with the defaults: @@ -1341,12 +1365,8 @@ public class Notification implements Parcelable mPriority = PRIORITY_DEFAULT; mPeople = new ArrayList<String>(); - // TODO: Decide on targetSdk from calling app whether to use quantum theme. - mQuantumTheme = true; - - // TODO: Decide on targetSdk from calling app whether to instantiate the processor at - // all. - mLegacyNotificationUtil = LegacyNotificationUtil.getInstance(); + mPreQuantum = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L; + mColorUtil = NotificationColorUtil.getInstance(); } /** @@ -1853,29 +1873,38 @@ public class Notification implements Parcelable } } + /** + * Sets {@link Notification#color}. + * + * @param argb The accent color to use + * + * @return The same Builder. + */ + public Builder setColor(int argb) { + mColor = argb; + return this; + } + private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) { RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId); boolean showLine3 = false; boolean showLine2 = false; int smallIconImageViewId = R.id.icon; - if (!mQuantumTheme && mPriority < PRIORITY_LOW) { - contentView.setInt(R.id.icon, - "setBackgroundResource", R.drawable.notification_template_icon_low_bg); - contentView.setInt(R.id.status_bar_latest_event_content, - "setBackgroundResource", R.drawable.notification_bg_low); + if (mPriority < PRIORITY_LOW) { + // TODO: Low priority presentation } if (mLargeIcon != null) { contentView.setImageViewBitmap(R.id.icon, mLargeIcon); - processLegacyLargeIcon(mLargeIcon, contentView); + processLargeIcon(mLargeIcon, contentView); smallIconImageViewId = R.id.right_icon; } if (mSmallIcon != 0) { contentView.setImageViewResource(smallIconImageViewId, mSmallIcon); contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE); if (mLargeIcon != null) { - processLegacySmallIcon(mSmallIcon, smallIconImageViewId, contentView); + processSmallRightIcon(mSmallIcon, smallIconImageViewId, contentView); } else { - processLegacyLargeIcon(mSmallIcon, contentView); + processSmallIconAsLarge(mSmallIcon, contentView); } } else { @@ -2035,12 +2064,12 @@ public class Notification implements Parcelable * doesn't create quantum notifications by itself) app. */ private boolean isLegacy() { - return mLegacyNotificationUtil != null; + return mColorUtil != null; } private void processLegacyAction(Action action, RemoteViews button) { if (isLegacy()) { - if (mLegacyNotificationUtil.isGrayscale(mContext, action.icon)) { + if (mColorUtil.isGrayscale(mContext, action.icon)) { button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0, mContext.getResources().getColor( R.color.notification_action_legacy_color_filter), @@ -2051,45 +2080,68 @@ public class Notification implements Parcelable private CharSequence processLegacyText(CharSequence charSequence) { if (isLegacy()) { - return mLegacyNotificationUtil.invertCharSequenceColors(charSequence); + return mColorUtil.invertCharSequenceColors(charSequence); } else { return charSequence; } } - private void processLegacyLargeIcon(int largeIconId, RemoteViews contentView) { - if (isLegacy()) { - processLegacyLargeIcon( - mLegacyNotificationUtil.isGrayscale(mContext, largeIconId), - contentView); + /** + * Apply any necessary background to smallIcons being used in the largeIcon spot. + */ + private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) { + if (!isLegacy() || mColorUtil.isGrayscale(mContext, largeIconId)) { + applyLargeIconBackground(contentView); } } - private void processLegacyLargeIcon(Bitmap largeIcon, RemoteViews contentView) { - if (isLegacy()) { - processLegacyLargeIcon( - mLegacyNotificationUtil.isGrayscale(largeIcon), - contentView); + /** + * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is, + * if it's grayscale). + */ + // TODO: also check bounds, transparency, that sort of thing. + private void processLargeIcon(Bitmap largeIcon, RemoteViews contentView) { + if (!isLegacy() || mColorUtil.isGrayscale(largeIcon)) { + applyLargeIconBackground(contentView); } } - private void processLegacyLargeIcon(boolean isGrayscale, RemoteViews contentView) { - if (isLegacy() && isGrayscale) { - contentView.setInt(R.id.icon, "setBackgroundResource", - R.drawable.notification_icon_legacy_bg_inset); - } + /** + * Add a colored circle behind the largeIcon slot. + */ + private void applyLargeIconBackground(RemoteViews contentView) { + contentView.setInt(R.id.icon, "setBackgroundResource", + R.drawable.notification_icon_legacy_bg_inset); + + contentView.setDrawableParameters( + R.id.icon, + true, + -1, + mColor, + PorterDuff.Mode.SRC_ATOP, + -1); } - private void processLegacySmallIcon(int smallIconDrawableId, int smallIconImageViewId, + /** + * Recolor small icons when used in the R.id.right_icon slot. + */ + private void processSmallRightIcon(int smallIconDrawableId, int smallIconImageViewId, RemoteViews contentView) { - if (isLegacy()) { - if (mLegacyNotificationUtil.isGrayscale(mContext, smallIconDrawableId)) { - contentView.setDrawableParameters(smallIconImageViewId, false, -1, - mContext.getResources().getColor( - R.color.notification_action_legacy_color_filter), - PorterDuff.Mode.MULTIPLY, -1); - } + 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); + } + } + + private int resolveColor() { + if (mColor == COLOR_DEFAULT) { + mColor = mContext.getResources().getColor(R.color.notification_icon_bg_color); + } else { + mColor |= 0xFF000000; // no alpha for custom colors } + return mColor; } /** @@ -2102,6 +2154,9 @@ public class Notification implements Parcelable n.icon = mSmallIcon; n.iconLevel = mSmallIconLevel; n.number = mNumber; + + n.color = resolveColor(); + n.contentView = makeContentView(); n.contentIntent = mContentIntent; n.deleteIntent = mDeleteIntent; @@ -2207,45 +2262,31 @@ public class Notification implements Parcelable private int getBaseLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_base - : R.layout.notification_template_base; + return R.layout.notification_template_quantum_base; } private int getBigBaseLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_big_base - : R.layout.notification_template_big_base; + return R.layout.notification_template_quantum_big_base; } private int getBigPictureLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_big_picture - : R.layout.notification_template_big_picture; + return R.layout.notification_template_quantum_big_picture; } private int getBigTextLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_big_text - : R.layout.notification_template_big_text; + return R.layout.notification_template_quantum_big_text; } private int getInboxLayoutResource() { - return mQuantumTheme - ? R.layout.notification_template_quantum_inbox - : R.layout.notification_template_inbox; + return R.layout.notification_template_quantum_inbox; } private int getActionLayoutResource() { - return mQuantumTheme - ? R.layout.notification_quantum_action - : R.layout.notification_action; + return R.layout.notification_quantum_action; } private int getActionTombstoneLayoutResource() { - return mQuantumTheme - ? R.layout.notification_quantum_action_tombstone - : R.layout.notification_action_tombstone; + return R.layout.notification_quantum_action_tombstone; } } diff --git a/core/res/res/drawable/stat_sys_adb.xml b/core/java/android/app/UsageStats.aidl index dfc8563..7dee70a 100644 --- a/core/res/res/drawable/stat_sys_adb.xml +++ b/core/java/android/app/UsageStats.aidl @@ -1,7 +1,5 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * Copyright 2013, 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. @@ -15,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ ---> -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/stat_sys_adb_am" - android:autoMirrored="true"> -</bitmap> +package android.app; + +parcelable UsageStats; +parcelable UsageStats.PackageStats; diff --git a/core/java/android/app/UsageStats.java b/core/java/android/app/UsageStats.java new file mode 100644 index 0000000..0aeba59 --- /dev/null +++ b/core/java/android/app/UsageStats.java @@ -0,0 +1,406 @@ +/* + * 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.res.Configuration; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.util.ArrayMap; + +import java.util.Map; + +/** + * Snapshot of current usage stats data. + * @hide + */ +public class UsageStats implements Parcelable { + /** @hide */ + public final ArrayMap<String, PackageStats> mPackages = new ArrayMap<String, PackageStats>(); + /** @hide */ + public final ArrayMap<Configuration, ConfigurationStats> mConfigurations + = new ArrayMap<Configuration, ConfigurationStats>(); + + public static class PackageStats implements Parcelable { + private final String mPackageName; + private int mLaunchCount; + private long mUsageTime; + private long mResumedTime; + + /** @hide */ + public final ArrayMap<String, Long> componentResumeTimes; + + public static final Parcelable.Creator<PackageStats> CREATOR + = new Parcelable.Creator<PackageStats>() { + public PackageStats createFromParcel(Parcel in) { + return new PackageStats(in); + } + + public PackageStats[] newArray(int size) { + return new PackageStats[size]; + } + }; + + public String toString() { + return "PackageStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + mPackageName + "}"; + } + + /** @hide */ + public PackageStats(String pkgName) { + mPackageName = pkgName; + componentResumeTimes = new ArrayMap<String, Long>(); + } + + /** @hide */ + public PackageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { + mPackageName = pkgName; + mLaunchCount = count; + mUsageTime = time; + componentResumeTimes = new ArrayMap<String, Long>(); + componentResumeTimes.putAll(lastResumeTimes); + } + + /** @hide */ + public PackageStats(Parcel source) { + mPackageName = source.readString(); + mLaunchCount = source.readInt(); + mUsageTime = source.readLong(); + final int N = source.readInt(); + componentResumeTimes = new ArrayMap<String, Long>(N); + for (int i = 0; i < N; i++) { + String component = source.readString(); + long lastResumeTime = source.readLong(); + componentResumeTimes.put(component, lastResumeTime); + } + } + + /** @hide */ + public PackageStats(PackageStats pStats) { + mPackageName = pStats.mPackageName; + mLaunchCount = pStats.mLaunchCount; + mUsageTime = pStats.mUsageTime; + componentResumeTimes = new ArrayMap<String, Long>(pStats.componentResumeTimes); + } + + /** @hide */ + public void resume(boolean launched) { + if (launched) { + mLaunchCount++; + } + mResumedTime = SystemClock.elapsedRealtime(); + } + + /** @hide */ + public void pause() { + if (mResumedTime > 0) { + mUsageTime += SystemClock.elapsedRealtime() - mResumedTime; + } + mResumedTime = 0; + } + + public final String getPackageName() { + return mPackageName; + } + + public final long getUsageTime(long elapsedRealtime) { + return mUsageTime + (mResumedTime > 0 ? (elapsedRealtime- mResumedTime) : 0); + } + + public final int getLaunchCount() { + return mLaunchCount; + } + + /** @hide */ + public boolean clearUsageTimes() { + mLaunchCount = 0; + mUsageTime = 0; + return mResumedTime <= 0 && componentResumeTimes.isEmpty(); + } + + public final int describeContents() { + return 0; + } + + public final void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcel(dest, parcelableFlags, 0); + } + + final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) { + dest.writeString(mPackageName); + dest.writeInt(mLaunchCount); + dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime); + dest.writeInt(componentResumeTimes.size()); + for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { + dest.writeString(ent.getKey()); + dest.writeLong(ent.getValue()); + } + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + } + } + + public static class ConfigurationStats implements Parcelable { + private final Configuration mConfiguration; + private long mLastUsedTime; + private int mUsageCount; + private long mUsageTime; + private long mStartedTime; + + public static final Parcelable.Creator<ConfigurationStats> CREATOR + = new Parcelable.Creator<ConfigurationStats>() { + public ConfigurationStats createFromParcel(Parcel in) { + return new ConfigurationStats(in); + } + + public ConfigurationStats[] newArray(int size) { + return new ConfigurationStats[size]; + } + }; + + public String toString() { + return "ConfigurationStats{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + mConfiguration + "}"; + } + + /** @hide */ + public ConfigurationStats(Configuration config) { + mConfiguration = config; + } + + /** @hide */ + public ConfigurationStats(Parcel source) { + mConfiguration = Configuration.CREATOR.createFromParcel(source); + mLastUsedTime = source.readLong(); + mUsageCount = source.readInt(); + mUsageTime = source.readLong(); + } + + /** @hide */ + public ConfigurationStats(ConfigurationStats pStats) { + mConfiguration = pStats.mConfiguration; + mLastUsedTime = pStats.mLastUsedTime; + mUsageCount = pStats.mUsageCount; + mUsageTime = pStats.mUsageTime; + } + + public final Configuration getConfiguration() { + return mConfiguration; + } + + public final long getLastUsedTime() { + return mLastUsedTime; + } + + public final long getUsageTime(long elapsedRealtime) { + return mUsageTime + (mStartedTime > 0 ? (elapsedRealtime- mStartedTime) : 0); + } + + public final int getUsageCount() { + return mUsageCount; + } + + /** @hide */ + public void start() { + mLastUsedTime = System.currentTimeMillis(); + mUsageCount++; + mStartedTime = SystemClock.elapsedRealtime(); + } + + /** @hide */ + public void stop() { + if (mStartedTime > 0) { + mUsageTime += SystemClock.elapsedRealtime() - mStartedTime; + } + mStartedTime = 0; + } + + /** @hide */ + public boolean clearUsageTimes() { + mUsageCount = 0; + mUsageTime = 0; + return mLastUsedTime == 0 && mStartedTime <= 0; + } + + public final int describeContents() { + return 0; + } + + public final void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcel(dest, parcelableFlags, 0); + } + + final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) { + mConfiguration.writeToParcel(dest, parcelableFlags); + dest.writeLong(mLastUsedTime); + dest.writeInt(mUsageCount); + dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime); + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + } + } + + /** @hide */ + public UsageStats() { + } + + /** @hide */ + public UsageStats(Parcel source, boolean extended) { + int N = source.readInt(); + for (int i=0; i<N; i++) { + PackageStats pkg = extended ? onNewPackageStats(source) : new PackageStats(source); + mPackages.put(pkg.getPackageName(), pkg); + } + N = source.readInt(); + for (int i=0; i<N; i++) { + ConfigurationStats config = extended ? onNewConfigurationStats(source) + : new ConfigurationStats(source); + mConfigurations.put(config.getConfiguration(), config); + } + } + + public int getPackageStatsCount() { + return mPackages.size(); + } + + public PackageStats getPackageStatsAt(int index) { + return mPackages.valueAt(index); + } + + public PackageStats getPackageStats(String pkgName) { + return mPackages.get(pkgName); + } + + /** @hide */ + public PackageStats getOrCreatePackageStats(String pkgName) { + PackageStats ps = mPackages.get(pkgName); + if (ps == null) { + ps = onNewPackageStats(pkgName); + mPackages.put(pkgName, ps); + } + return ps; + } + + public int getConfigurationStatsCount() { + return mConfigurations.size(); + } + + public ConfigurationStats getConfigurationStatsAt(int index) { + return mConfigurations.valueAt(index); + } + + public ConfigurationStats getConfigurationStats(Configuration config) { + return mConfigurations.get(config); + } + + /** @hide */ + public ConfigurationStats getOrCreateConfigurationStats(Configuration config) { + ConfigurationStats cs = mConfigurations.get(config); + if (cs == null) { + cs = onNewConfigurationStats(config); + mConfigurations.put(config, cs); + } + return cs; + } + + /** @hide */ + public void clearUsageTimes() { + for (int i=mPackages.size()-1; i>=0; i--) { + if (mPackages.valueAt(i).clearUsageTimes()) { + mPackages.removeAt(i); + } + } + for (int i=mConfigurations.size()-1; i>=0; i--) { + if (mConfigurations.valueAt(i).clearUsageTimes()) { + mConfigurations.removeAt(i); + } + } + } + + /** @hide */ + public PackageStats onNewPackageStats(String pkgName) { + return new PackageStats(pkgName); + } + + /** @hide */ + public PackageStats onNewPackageStats(Parcel source) { + return new PackageStats(source); + } + + /** @hide */ + public ConfigurationStats onNewConfigurationStats(Configuration config) { + return new ConfigurationStats(config); + } + + /** @hide */ + public ConfigurationStats onNewConfigurationStats(Parcel source) { + return new ConfigurationStats(source); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int parcelableFlags) { + writeToParcelInner(dest, parcelableFlags, false); + } + + /** @hide */ + public void writeExtendedToParcel(Parcel dest, int parcelableFlags) { + writeToParcelInner(dest, parcelableFlags, true); + } + + private void writeToParcelInner(Parcel dest, int parcelableFlags, boolean extended) { + final long elapsedRealtime = SystemClock.elapsedRealtime(); + + int N = mPackages.size(); + dest.writeInt(N); + for (int i=0; i<N; i++) { + PackageStats ps = mPackages.valueAt(i); + ps.writeToParcel(dest, parcelableFlags, elapsedRealtime); + if (extended) { + ps.writeExtendedToParcel(dest, parcelableFlags); + } + } + N = mConfigurations.size(); + dest.writeInt(N); + for (int i=0; i<N; i++) { + ConfigurationStats cs = mConfigurations.valueAt(i); + cs.writeToParcel(dest, parcelableFlags, elapsedRealtime); + if (extended) { + cs.writeExtendedToParcel(dest, parcelableFlags); + } + } + } + + public static final Parcelable.Creator<UsageStats> CREATOR + = new Parcelable.Creator<UsageStats>() { + public UsageStats createFromParcel(Parcel in) { + return new UsageStats(in, false); + } + + public UsageStats[] newArray(int size) { + return new UsageStats[size]; + } + }; +} diff --git a/core/java/android/app/UsageStatsManager.java b/core/java/android/app/UsageStatsManager.java new file mode 100644 index 0000000..fbf9c3b --- /dev/null +++ b/core/java/android/app/UsageStatsManager.java @@ -0,0 +1,51 @@ +/* + * 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.Context; +import android.os.ParcelableParcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import com.android.internal.app.IUsageStats; + +/** + * Access to usage stats data. + * @hide + */ +public class UsageStatsManager { + final Context mContext; + final IUsageStats mService; + + /** @hide */ + public UsageStatsManager(Context context) { + mContext = context; + mService = IUsageStats.Stub.asInterface(ServiceManager.getService( + Context.USAGE_STATS_SERVICE)); + } + + public UsageStats getCurrentStats() { + try { + ParcelableParcel in = mService.getCurrentStats(mContext.getOpPackageName()); + if (in != null) { + return new UsageStats(in.getParcel(), false); + } + } catch (RemoteException e) { + // About to die. + } + return new UsageStats(); + } +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 929bf65..24bb2cc 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -22,6 +22,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; @@ -2041,4 +2042,134 @@ public class DevicePolicyManager { } } } + + /** + * Called by profile or device owner to re-enable a system app that was disabled by default + * when the managed profile was created. This should only be called from a profile or device + * owner running within a managed profile. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName The package to be re-enabled in the current profile. + */ + public void enableSystemApp(ComponentName admin, String packageName) { + if (mService != null) { + try { + mService.enableSystemApp(admin, packageName); + } catch (RemoteException e) { + Log.w(TAG, "Failed to install package: " + packageName); + } + } + } + + /** + * Called by a profile owner to disable account management for a specific type of account. + * + * <p>The calling device admin must be a profile owner. If it is not, a + * security exception will be thrown. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param accountType For which account management is disabled or enabled. + * @param disabled The boolean indicating that account management will be disabled (true) or + * enabled (false). + */ + public void setAccountManagementDisabled(ComponentName admin, String accountType, + boolean disabled) { + if (mService != null) { + try { + mService.setAccountManagementDisabled(admin, accountType, disabled); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * Called by profile or device owner to re-enable system apps by intent that were disabled + * by default when the managed profile was created. This should only be called from a profile + * or device owner running within a managed profile. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param intent An intent matching the app(s) to be installed. All apps that resolve for this + * intent will be re-enabled in the current profile. + * @return int The number of activities that matched the intent and were installed. + */ + public int enableSystemApp(ComponentName admin, Intent intent) { + if (mService != null) { + try { + return mService.enableSystemAppWithIntent(admin, intent); + } catch (RemoteException e) { + Log.w(TAG, "Failed to install packages matching filter: " + intent); + } + } + return 0; + } + + /** + * Gets the array of accounts for which account management is disabled by the profile owner. + * + * <p> Account management can be disabled/enabled by calling + * {@link #setAccountManagementDisabled}. + * + * @return a list of account types for which account management has been disabled. + * + * @see #setAccountManagementDisabled + */ + public String[] getAccountTypesWithManagementDisabled() { + if (mService != null) { + try { + return mService.getAccountTypesWithManagementDisabled(); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + + return null; + } + + /** + * Sets which components may enter lock task mode. + * + * This function can only be called by the device owner or the profile owner. + * @param components The list of components allowed to enter lock task mode + */ + public void setLockTaskComponents(ComponentName[] components) throws SecurityException { + if (mService != null) { + try { + mService.setLockTaskComponents(components); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } + + /** + * This function returns the list of components allowed to start the lock task mode. + * @hide + */ + public ComponentName[] getLockTaskComponents() { + if (mService != null) { + try { + return mService.getLockTaskComponents(); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return null; + } + + /** + * This function lets the caller know whether the given component is allowed to start the + * lock task mode. + * @param component The component to check + */ + public boolean isLockTaskPermitted(ComponentName component) { + if (mService != null) { + try { + return mService.isLockTaskPermitted(component); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return false; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e3090b6..03ced0f 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -18,6 +18,7 @@ package android.app.admin; import android.content.ComponentName; +import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.RemoteCallback; @@ -122,4 +123,14 @@ interface IDevicePolicyManager { void setUserRestriction(in ComponentName who, in String key, boolean enable); void addForwardingIntentFilter(in ComponentName admin, in IntentFilter filter, int flags); void clearForwardingIntentFilters(in ComponentName admin); + + void enableSystemApp(in ComponentName admin, in String packageName); + int enableSystemAppWithIntent(in ComponentName admin, in Intent intent); + + void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled); + String[] getAccountTypesWithManagementDisabled(); + + void setLockTaskComponents(in ComponentName[] components); + ComponentName[] getLockTaskComponents(); + boolean isLockTaskPermitted(in ComponentName component); } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index a396a05..7f8d0ab 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -512,6 +512,25 @@ public final class BluetoothDevice implements Parcelable { */ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + /** + * No preferrence of physical transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_AUTO = 0; + + /** + * Prefer BR/EDR transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_BREDR = 1; + + /** + * Prefer LE transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_LE = 2; + + /** * Lazy initialization. Guaranteed final after first object constructed, or * getService() called. @@ -1216,6 +1235,27 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @throws IllegalArgumentException if callback is null + * @hide + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback, int transport) { // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -1226,10 +1266,11 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this); + BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this, transport); gatt.connect(autoConnect, callback); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } + } diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index ff3af7c..601d9ee 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -51,6 +51,7 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; + private int mTransport; private static final int CONN_STATE_IDLE = 0; private static final int CONN_STATE_CONNECTING = 1; @@ -135,7 +136,7 @@ public final class BluetoothGatt implements BluetoothProfile { } try { mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect); // autoConnect is inverse of "isDirect" + !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -600,10 +601,12 @@ public final class BluetoothGatt implements BluetoothProfile { } }; - /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { + /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, + int transport) { mContext = context; mService = iGatt; mDevice = device; + mTransport = transport; mServices = new ArrayList<BluetoothGattService>(); mConnState = CONN_STATE_IDLE; @@ -759,7 +762,7 @@ public final class BluetoothGatt implements BluetoothProfile { public boolean connect() { try { mService.clientConnect(mClientIf, mDevice.getAddress(), - false); // autoConnect is inverse of "isDirect" + false, mTransport); // autoConnect is inverse of "isDirect" return true; } catch (RemoteException e) { Log.e(TAG,"",e); diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 0c00c06..34e8605 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -50,6 +50,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private Object mServerIfLock = new Object(); private int mServerIf; + private int mTransport; private List<BluetoothGattService> mServices; private static final int CALLBACK_REG_TIMEOUT = 10000; @@ -269,12 +270,13 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) { + /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) { mContext = context; mService = iGatt; mAdapter = BluetoothAdapter.getDefaultAdapter(); mCallback = null; mServerIf = 0; + mTransport = transport; mServices = new ArrayList<BluetoothGattService>(); } @@ -401,7 +403,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.serverConnect(mServerIf, device.getAddress(), - autoConnect ? false : true); // autoConnect is inverse of "isDirect" + autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); return false; diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 172f3bc..b1618cf3 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -194,6 +194,26 @@ public final class BluetoothManager { */ public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback) { + + return (openGattServer (context, callback, BluetoothDevice.TRANSPORT_AUTO)); + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback,int transport) { if (context == null || callback == null) { throw new IllegalArgumentException("null parameter: " + context + " " + callback); } @@ -208,7 +228,7 @@ public final class BluetoothManager { Log.e(TAG, "Fail to get GATT Server connection"); return null; } - BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt); + BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt,transport); Boolean regStatus = mGattServer.registerCallback(callback); return regStatus? mGattServer : null; } catch (RemoteException e) { diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index f532f7c..00fd7ce 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -325,6 +325,7 @@ public final class BluetoothSocket implements Closeable { } } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); + throw new IOException("unable to send RPC: " + e.getMessage()); } } diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index 6dd551e..a0b603e 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -53,6 +53,9 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { private static final boolean DBG = true; private static final boolean VDBG = true; + // Event sent to the mBtdtHandler when DHCP fails so we can tear down the network. + private static final int EVENT_NETWORK_FAILED = 1; + private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); @@ -315,6 +318,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { } if (!success) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); + mBtdtHandler.obtainMessage(EVENT_NETWORK_FAILED).sendToTarget(); return; } mLinkProperties = dhcpResults.linkProperties; @@ -407,6 +411,10 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); mBtdt.stopReverseTether(); break; + case EVENT_NETWORK_FAILED: + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_FAILED"); + mBtdt.teardown(); + break; } } } diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index 4b28516..ab53fb0 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -18,6 +18,8 @@ package android.bluetooth; import android.os.ParcelUuid; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.Arrays; import java.util.HashSet; import java.util.UUID; @@ -76,6 +78,12 @@ public final class BluetoothUuid { public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); + /** Length of bytes for 16 bit UUID */ + public static final int UUID_BYTES_16_BIT = 2; + /** Length of bytes for 32 bit UUID */ + public static final int UUID_BYTES_32_BIT = 4; + /** Length of bytes for 128 bit UUID */ + public static final int UUID_BYTES_128_BIT = 16; public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -216,15 +224,60 @@ public final class BluetoothUuid { } /** + * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID, + * but the returned UUID is always in 128-bit format. + * Note UUID is little endian in Bluetooth. + * + * @param uuidBytes Byte representation of uuid. + * @return {@link ParcelUuid} parsed from bytes. + * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed. + */ + public static ParcelUuid parseUuidFrom(byte[] uuidBytes) { + if (uuidBytes == null) { + throw new IllegalArgumentException("uuidBytes cannot be null"); + } + int length = uuidBytes.length; + if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT && + length != UUID_BYTES_128_BIT) { + throw new IllegalArgumentException("uuidBytes length invalid - " + length); + } + + // Construct a 128 bit UUID. + if (length == UUID_BYTES_128_BIT) { + ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); + long msb = buf.getLong(8); + long lsb = buf.getLong(0); + return new ParcelUuid(new UUID(msb, lsb)); + } + + // For 16 bit and 32 bit UUID we need to convert them to 128 bit value. + // 128_bit_value = uuid * 2^96 + BASE_UUID + long shortUuid; + if (length == UUID_BYTES_16_BIT) { + shortUuid = uuidBytes[0] & 0xFF; + shortUuid += (uuidBytes[1] & 0xFF) << 8; + } else { + shortUuid = uuidBytes[0] & 0xFF ; + shortUuid += (uuidBytes[1] & 0xFF) << 8; + shortUuid += (uuidBytes[2] & 0xFF) << 16; + shortUuid += (uuidBytes[3] & 0xFF) << 24; + } + long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32); + long lsb = BASE_UUID.getUuid().getLeastSignificantBits(); + return new ParcelUuid(new UUID(msb, lsb)); + } + + /** * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. + * * @param parcelUuid * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. */ public static boolean isShortUuid(ParcelUuid parcelUuid) { - UUID uuid = parcelUuid.getUuid(); - if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { - return false; - } - return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); } } diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index c6b5c3d..49b156d 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -35,7 +35,7 @@ interface IBluetoothGatt { void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect); + void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); void clientDisconnect(in int clientIf, in String address); void startAdvertising(in int appIf); void stopAdvertising(); @@ -77,7 +77,7 @@ interface IBluetoothGatt { void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); - void serverConnect(in int servertIf, in String address, in boolean isDirect); + void serverConnect(in int servertIf, in String address, in boolean isDirect, in int transport); void serverDisconnect(in int serverIf, in String address); void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles, diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index de223a3..a059e48 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1982,6 +1982,7 @@ public abstract class Context { WIFI_SERVICE, WIFI_HOTSPOT_SERVICE, WIFI_P2P_SERVICE, + WIFI_SCANNING_SERVICE, NSD_SERVICE, AUDIO_SERVICE, MEDIA_ROUTER_SERVICE, @@ -2054,6 +2055,9 @@ public abstract class Context { * <dt> {@link #WIFI_SERVICE} ("wifi") * <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of * Wi-Fi connectivity. + * <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p") + * <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of + * Wi-Fi Direct connectivity. * <dt> {@link #INPUT_METHOD_SERVICE} ("input_method") * <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager} * for management of input methods. @@ -2357,6 +2361,16 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a {@link + * android.net.wifi.WifiScanner} for scanning the wifi universe + * + * @see #getSystemService + * @see android.net.wifi.WifiScanner + * @hide + */ + public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; + + /** + * Use with {@link #getSystemService} to retrieve a {@link * android.net.nsd.NsdManager} for handling management of network service * discovery * @@ -2377,6 +2391,16 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a + * {@link android.service.fingerprint.FingerprintManager} for handling management + * of fingerprints. + * + * @see #getSystemService + * @see android.app.FingerprintManager + */ + public static final String FINGERPRINT_SERVICE = "fingerprint"; + + /** + * Use with {@link #getSystemService} to retrieve a * {@link android.media.MediaRouter} for controlling and managing * routing of media. * @@ -2659,6 +2683,16 @@ public abstract class Context { public static final String NETWORK_SCORE_SERVICE = "network_score"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.app.UsageStatsManager} for interacting with the status bar. + * + * @see #getSystemService + * @see android.app.UsageStatsManager + * @hide + */ + public static final String USAGE_STATS_SERVICE = "usagestats"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ae5437b..3cfc56c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3736,7 +3736,8 @@ public class Intent implements Parcelable, Cloneable { * * <p>When set, the activity specified by this Intent will launch into a * separate task rooted at that activity. The activity launched must be - * defined with {@link android.R.attr#launchMode} "standard" or "singleTop". + * defined with {@link android.R.attr#launchMode} <code>standard</code> + * or <code>singleTop</code>. * * <p>If FLAG_ACTIVITY_NEW_DOCUMENT is used without * {@link #FLAG_ACTIVITY_MULTIPLE_TASK} then the activity manager will @@ -3751,6 +3752,8 @@ public class Intent implements Parcelable, Cloneable { * always create a new task. Thus the same document may be made to appear * more than one time in Recents. * + * <p>This is equivalent to the attribute {@link android.R.attr#documentLaunchMode}. + * * @see #FLAG_ACTIVITY_MULTIPLE_TASK */ public static final int FLAG_ACTIVITY_NEW_DOCUMENT = @@ -3815,6 +3818,15 @@ public class Intent implements Parcelable, Cloneable { */ public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000; /** + * If set and the new activity is the root of a new task, then the task + * will remain in the list of recently launched tasks only until all of + * the activities in it are finished. + * + * <p>This is equivalent to the attribute + * {@link android.R.styleable#AndroidManifestActivity_autoRemoveFromRecents}. + */ + public static final int FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS = 0x00002000; + /** * If set, when sending a broadcast only registered receivers will be * called -- no BroadcastReceiver components will be launched. */ @@ -4019,7 +4031,7 @@ public class Intent implements Parcelable, Cloneable { /** * Create an intent for a specific component with a specified action and data. - * This is equivalent using {@link #Intent(String, android.net.Uri)} to + * This is equivalent to using {@link #Intent(String, android.net.Uri)} to * construct the Intent and then calling {@link #setClass} to set its * class. * diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java index a9a62a7..9ba45ca 100644 --- a/core/java/android/content/SyncRequest.java +++ b/core/java/android/content/SyncRequest.java @@ -473,7 +473,7 @@ public class SyncRequest implements Parcelable { * SyncRequest.Builder builder = * new SyncRequest.Builder() * .setSyncAdapter(dummyAccount, dummyProvider) - * .syncOnce(5 * MINUTES_IN_SECS); + * .syncOnce(); * * for (String syncData : syncItems) { * Bundle extras = new Bundle(); diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index c53e545..c2fe3a2 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -67,7 +67,37 @@ public class ActivityInfo extends ComponentInfo * {@link #LAUNCH_SINGLE_INSTANCE}. */ public int launchMode; - + + /** + * Constant corresponding to <code>none</code> in + * the {@link android.R.attr#documentLaunchMode} attribute. + */ + public static final int DOCUMENT_LAUNCH_NONE = 0; + /** + * Constant corresponding to <code>intoExisting</code> in + * the {@link android.R.attr#documentLaunchMode} attribute. + */ + public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; + /** + * Constant corresponding to <code>always</code> in + * the {@link android.R.attr#documentLaunchMode} attribute. + */ + public static final int DOCUMENT_LAUNCH_ALWAYS = 2; + /** + * The document launch mode style requested by the activity. From the + * {@link android.R.attr#documentLaunchMode} attribute, one of + * {@link #DOCUMENT_LAUNCH_NONE}, {@link #DOCUMENT_LAUNCH_INTO_EXISTING}, + * {@link #DOCUMENT_LAUNCH_ALWAYS}. + * + * <p>Modes DOCUMENT_LAUNCH_ALWAYS + * and DOCUMENT_LAUNCH_INTO_EXISTING are equivalent to {@link + * android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT + * Intent.FLAG_ACTIVITY_NEW_DOCUMENT} with and without {@link + * android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK + * Intent.FLAG_ACTIVITY_MULTIPLE_TASK} respectively. + */ + public int documentLaunchMode; + /** * Optional name of a permission required to be able to access this * Activity. From the "permission" attribute. @@ -192,10 +222,15 @@ public class ActivityInfo extends ComponentInfo * Bit in {@link #flags} indicating that this activity is to be persisted across * reboots for display in the Recents list. * {@link android.R.attr#persistable} - * @hide */ public static final int FLAG_PERSISTABLE = 0x1000; /** + * Bit in {@link #flags} indicating that tasks started with this activity are to be + * removed from the recent list of tasks when the last activity in the task is finished. + * {@link android.R.attr#autoRemoveFromRecents} + */ + public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 0x2000; + /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the primary user. Only works with broadcast receivers. Set from the * android.R.attr#primaryUserOnly attribute. diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 1a003ff..eb2c11f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -936,13 +936,21 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has at least one camera pointing in - * some direction. + * some direction, or can support an external camera being connected to it. */ @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any"; /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device can support having an external camera connected to it. + * The external camera may not always be connected or available to applications to use. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device's camera supports flash. */ @SdkConstant(SdkConstantType.FEATURE) @@ -1083,6 +1091,13 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device includes a heart rate monitor. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has a telephony radio with data * communication support. */ diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 080b37b..ff96c51 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2462,17 +2462,6 @@ public class PackageParser { a.info.flags |= ActivityInfo.FLAG_IMMERSIVE; } - if (sa.getBoolean( - com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) { - a.info.flags |= ActivityInfo.FLAG_PERSISTABLE; - } - - if (sa.getBoolean( - com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded, - false)) { - a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED; - } - if (!receiver) { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated, @@ -2483,6 +2472,9 @@ public class PackageParser { a.info.launchMode = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE); + a.info.documentLaunchMode = sa.getInt( + com.android.internal.R.styleable.AndroidManifestActivity_documentLaunchMode, + ActivityInfo.DOCUMENT_LAUNCH_NONE); a.info.screenOrientation = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); @@ -2492,6 +2484,23 @@ public class PackageParser { a.info.softInputMode = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode, 0); + + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) { + a.info.flags |= ActivityInfo.FLAG_PERSISTABLE; + } + + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded, + false)) { + a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED; + } + + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_autoRemoveFromRecents, + false)) { + a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS; + } } else { a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE; a.info.configChanges = 0; @@ -3591,10 +3600,13 @@ public class PackageParser { // For use by the package manager to keep track of the path to the // file an app came from. public String mScanPath; - - // For use by package manager to keep track of where it has done dexopt. - public boolean mDidDexOpt; - + + // For use by package manager to keep track of where it needs to do dexopt. + public boolean mDexOptNeeded = true; + + // For use by package manager to keep track of when a package was last used. + public long mLastPackageUsageTimeInMills; + // // User set enabled state. // public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; // diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 28309d7..5f2af8c 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1277,20 +1277,6 @@ public final class CameraCharacteristics extends CameraMetadata { new Key<Integer>("android.sensor.orientation", int.class); /** - * <p>The number of input samples for each dimension of - * {@link CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP android.sensor.profileHueSatMap}.</p> - * <p>The number of input samples for the hue, saturation, and value - * dimension of {@link CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP android.sensor.profileHueSatMap}. The order of the - * dimensions given is hue, saturation, value; where hue is the 0th - * element.</p> - * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> - * - * @see CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP - */ - public static final Key<int[]> SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS = - new Key<int[]>("android.sensor.profileHueSatMapDimensions", int[].class); - - /** * <p>Optional. Defaults to [OFF]. Lists the supported test * pattern modes for {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode}.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 1d2d0e9..51ea447 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -1906,36 +1906,6 @@ public final class CaptureResult extends CameraMetadata { new Key<Rational[]>("android.sensor.neutralColorPoint", Rational[].class); /** - * <p>A mapping containing a hue shift, saturation scale, and value scale - * for each pixel.</p> - * <p>hue_samples, saturation_samples, and value_samples are given in - * {@link CameraCharacteristics#SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS android.sensor.profileHueSatMapDimensions}.</p> - * <p>Each entry of this map contains three floats corresponding to the - * hue shift, saturation scale, and value scale, respectively; where the - * hue shift has the lowest index. The map entries are stored in the tag - * in nested loop order, with the value divisions in the outer loop, the - * hue divisions in the middle loop, and the saturation divisions in the - * inner loop. All zero input saturation entries are required to have a - * value scale factor of 1.0.</p> - * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> - * - * @see CameraCharacteristics#SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS - */ - public static final Key<float[]> SENSOR_PROFILE_HUE_SAT_MAP = - new Key<float[]>("android.sensor.profileHueSatMap", float[].class); - - /** - * <p>A list of x,y samples defining a tone-mapping curve for gamma adjustment.</p> - * <p>This tag contains a default tone curve that can be applied while - * processing the image as a starting point for user adjustments. - * The curve is specified as a list of value pairs in linear gamma. - * The curve is interpolated using a cubic spline.</p> - * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> - */ - public static final Key<float[]> SENSOR_PROFILE_TONE_CURVE = - new Key<float[]>("android.sensor.profileToneCurve", float[].class); - - /** * <p>The worst-case divergence between Bayer green channels.</p> * <p>This value is an estimate of the worst case split between the * Bayer green channels in the red and blue rows in the sensor color diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 3da00b1..30d7043 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -1307,14 +1307,13 @@ public class ConnectivityManager { * doing something unusual like general internal filtering this may be useful. On * a private network where the proxy is not accessible, you may break HTTP using this. * - * @param p The a {@link ProxyProperties} object defining the new global + * @param p The a {@link ProxyInfo} object defining the new global * HTTP proxy. A {@code null} value will clear the global HTTP proxy. * * <p>This method requires the call to hold the permission - * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}. - * {@hide} + * android.Manifest.permission#CONNECTIVITY_INTERNAL. */ - public void setGlobalProxy(ProxyProperties p) { + public void setGlobalProxy(ProxyInfo p) { try { mService.setGlobalProxy(p); } catch (RemoteException e) { @@ -1324,14 +1323,13 @@ public class ConnectivityManager { /** * Retrieve any network-independent global HTTP proxy. * - * @return {@link ProxyProperties} for the current global HTTP proxy or {@code null} + * @return {@link ProxyInfo} for the current global HTTP proxy or {@code null} * if no global HTTP proxy is set. * * <p>This method requires the call to hold the permission * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. - * {@hide} */ - public ProxyProperties getGlobalProxy() { + public ProxyInfo getGlobalProxy() { try { return mService.getGlobalProxy(); } catch (RemoteException e) { @@ -1343,14 +1341,14 @@ public class ConnectivityManager { * Get the HTTP proxy settings for the current default network. Note that * if a global proxy is set, it will override any per-network setting. * - * @return the {@link ProxyProperties} for the current HTTP proxy, or {@code null} if no + * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no * HTTP proxy is active. * * <p>This method requires the call to hold the permission * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. * {@hide} */ - public ProxyProperties getProxy() { + public ProxyInfo getProxy() { try { return mService.getProxy(); } catch (RemoteException e) { diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 381a817..d53a856 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -21,7 +21,7 @@ import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkQuotaInfo; import android.net.NetworkState; -import android.net.ProxyProperties; +import android.net.ProxyInfo; import android.os.IBinder; import android.os.Messenger; import android.os.ParcelFileDescriptor; @@ -107,11 +107,11 @@ interface IConnectivityManager void reportInetCondition(int networkType, int percentage); - ProxyProperties getGlobalProxy(); + ProxyInfo getGlobalProxy(); - void setGlobalProxy(in ProxyProperties p); + void setGlobalProxy(in ProxyInfo p); - ProxyProperties getProxy(); + ProxyInfo getProxy(); void setDataDependency(int networkType, boolean met); diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 4dfd3d9..2dcc544 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -16,7 +16,7 @@ package android.net; -import android.net.ProxyProperties; +import android.net.ProxyInfo; import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; @@ -65,7 +65,7 @@ public class LinkProperties implements Parcelable { private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>(); private String mDomains; private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); - private ProxyProperties mHttpProxy; + private ProxyInfo mHttpProxy; private int mMtu; // Stores the properties of links that are "stacked" above this link. @@ -101,7 +101,7 @@ public class LinkProperties implements Parcelable { mDomains = source.getDomains(); for (RouteInfo r : source.getRoutes()) mRoutes.add(r); mHttpProxy = (source.getHttpProxy() == null) ? - null : new ProxyProperties(source.getHttpProxy()); + null : new ProxyInfo(source.getHttpProxy()); for (LinkProperties l: source.mStackedLinks.values()) { addStackedLink(l); } @@ -295,10 +295,10 @@ public class LinkProperties implements Parcelable { return routes; } - public void setHttpProxy(ProxyProperties proxy) { + public void setHttpProxy(ProxyInfo proxy) { mHttpProxy = proxy; } - public ProxyProperties getHttpProxy() { + public ProxyInfo getHttpProxy() { return mHttpProxy; } @@ -720,7 +720,7 @@ public class LinkProperties implements Parcelable { netProp.addRoute((RouteInfo)in.readParcelable(null)); } if (in.readByte() == 1) { - netProp.setHttpProxy((ProxyProperties)in.readParcelable(null)); + netProp.setHttpProxy((ProxyInfo)in.readParcelable(null)); } ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>(); in.readList(stackedLinks, LinkProperties.class.getClassLoader()); diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java index bea8d1c..8f41e85 100644 --- a/core/java/android/net/Proxy.java +++ b/core/java/android/net/Proxy.java @@ -19,6 +19,7 @@ package android.net; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; +import android.net.ProxyInfo; import android.text.TextUtils; import android.util.Log; @@ -63,8 +64,11 @@ public final class Proxy { */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE"; - /** {@hide} **/ - public static final String EXTRA_PROXY_INFO = "proxy"; + /** + * Intent extra included with {@link #PROXY_CHANGE_ACTION} intents. + * It describes the new proxy being used (as a {@link ProxyInfo} object). + */ + public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO"; /** @hide */ public static final int PROXY_VALID = 0; @@ -114,24 +118,14 @@ public final class Proxy { */ public static final java.net.Proxy getProxy(Context ctx, String url) { String host = ""; - if (url != null) { + if ((url != null) && !isLocalHost(host)) { URI uri = URI.create(url); - host = uri.getHost(); - } + ProxySelector proxySelector = ProxySelector.getDefault(); - if (!isLocalHost(host)) { - if (sConnectivityManager == null) { - sConnectivityManager = (ConnectivityManager)ctx.getSystemService( - Context.CONNECTIVITY_SERVICE); - } - if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY; - - ProxyProperties proxyProperties = sConnectivityManager.getProxy(); + List<java.net.Proxy> proxyList = proxySelector.select(uri); - if (proxyProperties != null) { - if (!proxyProperties.isExcluded(host)) { - return proxyProperties.makeProxy(); - } + if (proxyList.size() > 0) { + return proxyList.get(0); } } return java.net.Proxy.NO_PROXY; @@ -275,7 +269,7 @@ public final class Proxy { } /** @hide */ - public static final void setHttpProxySystemProperty(ProxyProperties p) { + public static final void setHttpProxySystemProperty(ProxyInfo p) { String host = null; String port = null; String exclList = null; @@ -283,8 +277,8 @@ public final class Proxy { if (p != null) { host = p.getHost(); port = Integer.toString(p.getPort()); - exclList = p.getExclusionList(); - pacFileUrl = p.getPacFileUrl(); + exclList = p.getExclusionListAsString(); + pacFileUrl = p.getPacFileUrl().toString(); } setHttpProxySystemProperty(host, port, exclList, pacFileUrl); } diff --git a/core/java/android/net/ProxyProperties.aidl b/core/java/android/net/ProxyInfo.aidl index 02ea15d..2c91960 100644 --- a/core/java/android/net/ProxyProperties.aidl +++ b/core/java/android/net/ProxyInfo.aidl @@ -17,5 +17,5 @@ package android.net; -parcelable ProxyProperties; +parcelable ProxyInfo; diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyInfo.java index 50f45e8..b40941f 100644 --- a/core/java/android/net/ProxyProperties.java +++ b/core/java/android/net/ProxyInfo.java @@ -21,14 +21,23 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import org.apache.http.client.HttpClient; + import java.net.InetSocketAddress; +import java.net.URLConnection; +import java.util.List; import java.util.Locale; /** - * A container class for the http proxy info - * @hide + * Describes a proxy configuration. + * + * Proxy configurations are already integrated within the Apache HTTP stack. + * So {@link URLConnection} and {@link HttpClient} will use them automatically. + * + * Other HTTP stacks will need to obtain the proxy info from + * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}. */ -public class ProxyProperties implements Parcelable { +public class ProxyInfo implements Parcelable { private String mHost; private int mPort; @@ -36,32 +45,82 @@ public class ProxyProperties implements Parcelable { private String[] mParsedExclusionList; private String mPacFileUrl; + /** + *@hide + */ public static final String LOCAL_EXCL_LIST = ""; + /** + *@hide + */ public static final int LOCAL_PORT = -1; + /** + *@hide + */ public static final String LOCAL_HOST = "localhost"; - public ProxyProperties(String host, int port, String exclList) { + /** + * Constructs a {@link ProxyInfo} object that points at a Direct proxy + * on the specified host and port. + */ + public static ProxyInfo buildDirectProxy(String host, int port) { + return new ProxyInfo(host, port, null); + } + + /** + * Constructs a {@link ProxyInfo} object that points at a Direct proxy + * on the specified host and port. + * + * The proxy will not be used to access any host in exclusion list, exclList. + * + * @param exclList Hosts to exclude using the proxy on connections for. These + * hosts can use wildcards such as *.example.com. + */ + public static ProxyInfo buildDirectProxy(String host, int port, List<String> exclList) { + String[] array = exclList.toArray(new String[exclList.size()]); + return new ProxyInfo(host, port, TextUtils.join(",", array), array); + } + + /** + * Construct a {@link ProxyInfo} that will download and run the PAC script + * at the specified URL. + */ + public static ProxyInfo buildPacProxy(Uri pacUri) { + return new ProxyInfo(pacUri.toString()); + } + + /** + * Create a ProxyProperties that points at a HTTP Proxy. + * @hide + */ + public ProxyInfo(String host, int port, String exclList) { mHost = host; mPort = port; setExclusionList(exclList); } - public ProxyProperties(String pacFileUrl) { + /** + * Create a ProxyProperties that points at a PAC URL. + * @hide + */ + public ProxyInfo(String pacFileUrl) { mHost = LOCAL_HOST; mPort = LOCAL_PORT; setExclusionList(LOCAL_EXCL_LIST); mPacFileUrl = pacFileUrl; } - // Only used in PacManager after Local Proxy is bound. - public ProxyProperties(String pacFileUrl, int localProxyPort) { + /** + * Only used in PacManager after Local Proxy is bound. + * @hide + */ + public ProxyInfo(String pacFileUrl, int localProxyPort) { mHost = LOCAL_HOST; mPort = localProxyPort; setExclusionList(LOCAL_EXCL_LIST); mPacFileUrl = pacFileUrl; } - private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) { + private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) { mHost = host; mPort = port; mExclusionList = exclList; @@ -70,16 +129,22 @@ public class ProxyProperties implements Parcelable { } // copy constructor instead of clone - public ProxyProperties(ProxyProperties source) { + /** + * @hide + */ + public ProxyInfo(ProxyInfo source) { if (source != null) { mHost = source.getHost(); mPort = source.getPort(); - mPacFileUrl = source.getPacFileUrl(); - mExclusionList = source.getExclusionList(); + mPacFileUrl = source.mPacFileUrl; + mExclusionList = source.getExclusionListAsString(); mParsedExclusionList = source.mParsedExclusionList; } } + /** + * @hide + */ public InetSocketAddress getSocketAddress() { InetSocketAddress inetSocketAddress = null; try { @@ -88,20 +153,46 @@ public class ProxyProperties implements Parcelable { return inetSocketAddress; } - public String getPacFileUrl() { - return mPacFileUrl; + /** + * Returns the URL of the current PAC script or null if there is + * no PAC script. + */ + public Uri getPacFileUrl() { + if (TextUtils.isEmpty(mPacFileUrl)) { + return null; + } + return Uri.parse(mPacFileUrl); } + /** + * When configured to use a Direct Proxy this returns the host + * of the proxy. + */ public String getHost() { return mHost; } + /** + * When configured to use a Direct Proxy this returns the port + * of the proxy + */ public int getPort() { return mPort; } - // comma separated - public String getExclusionList() { + /** + * When configured to use a Direct Proxy this returns the list + * of hosts for which the proxy is ignored. + */ + public String[] getExclusionList() { + return mParsedExclusionList; + } + + /** + * comma separated + * @hide + */ + public String getExclusionListAsString() { return mExclusionList; } @@ -111,33 +202,13 @@ public class ProxyProperties implements Parcelable { if (mExclusionList == null) { mParsedExclusionList = new String[0]; } else { - String splitExclusionList[] = exclusionList.toLowerCase(Locale.ROOT).split(","); - mParsedExclusionList = new String[splitExclusionList.length * 2]; - for (int i = 0; i < splitExclusionList.length; i++) { - String s = splitExclusionList[i].trim(); - if (s.startsWith(".")) s = s.substring(1); - mParsedExclusionList[i*2] = s; - mParsedExclusionList[(i*2)+1] = "." + s; - } + mParsedExclusionList = exclusionList.toLowerCase(Locale.ROOT).split(","); } } - public boolean isExcluded(String url) { - if (TextUtils.isEmpty(url) || mParsedExclusionList == null || - mParsedExclusionList.length == 0) return false; - - Uri u = Uri.parse(url); - String urlDomain = u.getHost(); - if (urlDomain == null) return false; - for (int i = 0; i< mParsedExclusionList.length; i+=2) { - if (urlDomain.equals(mParsedExclusionList[i]) || - urlDomain.endsWith(mParsedExclusionList[i+1])) { - return true; - } - } - return false; - } - + /** + * @hide + */ public boolean isValid() { if (!TextUtils.isEmpty(mPacFileUrl)) return true; return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost, @@ -145,6 +216,9 @@ public class ProxyProperties implements Parcelable { mExclusionList == null ? "" : mExclusionList); } + /** + * @hide + */ public java.net.Proxy makeProxy() { java.net.Proxy proxy = java.net.Proxy.NO_PROXY; if (mHost != null) { @@ -179,17 +253,17 @@ public class ProxyProperties implements Parcelable { @Override public boolean equals(Object o) { - if (!(o instanceof ProxyProperties)) return false; - ProxyProperties p = (ProxyProperties)o; + if (!(o instanceof ProxyInfo)) return false; + ProxyInfo p = (ProxyInfo)o; // If PAC URL is present in either then they must be equal. // Other parameters will only be for fall back. if (!TextUtils.isEmpty(mPacFileUrl)) { return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort; } - if (!TextUtils.isEmpty(p.getPacFileUrl())) { + if (!TextUtils.isEmpty(p.mPacFileUrl)) { return false; } - if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false; + if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) return false; if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) { return false; } @@ -245,15 +319,15 @@ public class ProxyProperties implements Parcelable { * Implement the Parcelable interface. * @hide */ - public static final Creator<ProxyProperties> CREATOR = - new Creator<ProxyProperties>() { - public ProxyProperties createFromParcel(Parcel in) { + public static final Creator<ProxyInfo> CREATOR = + new Creator<ProxyInfo>() { + public ProxyInfo createFromParcel(Parcel in) { String host = null; int port = 0; if (in.readByte() != 0) { String url = in.readString(); int localPort = in.readInt(); - return new ProxyProperties(url, localPort); + return new ProxyInfo(url, localPort); } if (in.readByte() != 0) { host = in.readString(); @@ -261,13 +335,13 @@ public class ProxyProperties implements Parcelable { } String exclList = in.readString(); String[] parsedExclList = in.readStringArray(); - ProxyProperties proxyProperties = - new ProxyProperties(host, port, exclList, parsedExclList); + ProxyInfo proxyProperties = + new ProxyInfo(host, port, exclList, parsedExclList); return proxyProperties; } - public ProxyProperties[] newArray(int size) { - return new ProxyProperties[size]; + public ProxyInfo[] newArray(int size) { + return new ProxyInfo[size]; } }; } diff --git a/core/java/com/android/internal/os/PkgUsageStats.aidl b/core/java/android/os/ParcelableParcel.aidl index 8305271..61f730c 100644 --- a/core/java/com/android/internal/os/PkgUsageStats.aidl +++ b/core/java/android/os/ParcelableParcel.aidl @@ -1,20 +1,19 @@ -/* //device/java/android/android/content/Intent.aidl +/* +** Copyright 2014, The Android Open Source Project ** -** Copyright 2007, The Android Open Source Project +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at ** -** 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 ** -** 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 +** 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.internal.os; +package android.os; -parcelable PkgUsageStats; +parcelable ParcelableParcel; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 1b2b798..e379621 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -91,7 +91,6 @@ public class UserManager { * @see #setUserRestrictions(Bundle) * @see #getUserRestrictions() */ - public static final String DISALLOW_SHARE_LOCATION = "no_share_location"; /** @@ -145,6 +144,128 @@ public class UserManager { */ public static final String DISALLOW_REMOVE_USER = "no_remove_user"; + /** + * Key for user restrictions. Specifies if a user is disallowed from enabling or + * accessing debugging features. The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring VPN. + * The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_VPN = "no_config_vpn"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring Tethering + * & portable hotspots. The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_TETHERING = "no_config_tethering"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from factory resetting + * from Settings. + * The default value is <code>false</code>. + * <p> + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_FACTORY_RESET = "no_factory_reset"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from adding new users and + * profiles. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_ADD_USER = "no_add_user"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from disabling application + * verification. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring cell + * broadcasts. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring mobile + * networks. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring + * applications in Settings. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_APPS = "no_config_apps"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from mounting + * physical external media. The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from adjusting microphone + * volume. + * The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from adjusting the master + * volume. + * The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume"; + /** @hide */ public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3; /** @hide */ @@ -572,7 +693,7 @@ public class UserManager { /** * Returns information for all users on this device. Requires * {@link android.Manifest.permission#MANAGE_USERS} permission. - * + * * @param excludeDying specify if the list should exclude users being * removed. * @return the list of users that were created. diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index e4f73cb..811751d 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -167,7 +167,7 @@ public final class PrintManager { /** * Callback notifying that a print job state changed. - * + * * @param printJobId The print job id. */ public void onPrintJobStateChanged(PrintJobId printJobId); @@ -175,7 +175,7 @@ public final class PrintManager { /** * Creates a new instance. - * + * * @param context The current context in which to operate. * @param service The backing system service. * @hide @@ -207,13 +207,17 @@ public final class PrintManager { /** * Creates an instance that can access all print jobs. - * + * * @param userId The user id for which to get all print jobs. * @return An instance if the caller has the permission to access all print * jobs, null otherwise. * @hide */ public PrintManager getGlobalPrintManagerForUser(int userId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } return new PrintManager(mContext, mService, userId, APP_ID_ANY); } @@ -228,11 +232,15 @@ public final class PrintManager { /** * Adds a listener for observing the state of print jobs. - * + * * @param listener The listener to add. * @hide */ public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } if (mPrintJobStateChangeListeners == null) { mPrintJobStateChangeListeners = new ArrayMap<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper>(); @@ -249,11 +257,15 @@ public final class PrintManager { /** * Removes a listener for observing the state of print jobs. - * + * * @param listener The listener to remove. * @hide */ public void removePrintJobStateChangeListener(PrintJobStateChangeListener listener) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } if (mPrintJobStateChangeListeners == null) { return; } @@ -275,12 +287,16 @@ public final class PrintManager { /** * Gets a print job given its id. - * + * * @return The print job list. * @see PrintJob * @hide */ public PrintJob getPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } try { PrintJobInfo printJob = mService.getPrintJobInfo(printJobId, mAppId, mUserId); if (printJob != null) { @@ -294,11 +310,15 @@ public final class PrintManager { /** * Gets the print jobs for this application. - * + * * @return The print job list. * @see PrintJob */ public List<PrintJob> getPrintJobs() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId); if (printJobInfos == null) { @@ -317,6 +337,10 @@ public final class PrintManager { } void cancelPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } try { mService.cancelPrintJob(printJobId, mAppId, mUserId); } catch (RemoteException re) { @@ -325,6 +349,10 @@ public final class PrintManager { } void restartPrintJob(PrintJobId printJobId) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return; + } try { mService.restartPrintJob(printJobId, mAppId, mUserId); } catch (RemoteException re) { @@ -383,6 +411,10 @@ public final class PrintManager { */ public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter, PrintAttributes attributes) { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } if (!(mContext instanceof Activity)) { throw new IllegalStateException("Can print only from an activity"); } @@ -418,11 +450,15 @@ public final class PrintManager { /** * Gets the list of enabled print services. - * + * * @return The enabled service list or an empty list. * @hide */ public List<PrintServiceInfo> getEnabledPrintServices() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId); if (enabledServices != null) { @@ -436,11 +472,15 @@ public final class PrintManager { /** * Gets the list of installed print services. - * + * * @return The installed service list or an empty list. * @hide */ public List<PrintServiceInfo> getInstalledPrintServices() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return Collections.emptyList(); + } try { List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId); if (installedServices != null) { @@ -456,6 +496,10 @@ public final class PrintManager { * @hide */ public PrinterDiscoverySession createPrinterDiscoverySession() { + if (mService == null) { + Log.w(LOG_TAG, "Feature android.software.print not available"); + return null; + } return new PrinterDiscoverySession(mService, mContext, mUserId); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0eb994d..dc618c8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2823,7 +2823,6 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.TETHER_SUPPORTED); MOVED_TO_GLOBAL.add(Settings.Global.USB_MASS_STORAGE_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.USE_GOOGLE_MAIL); - MOVED_TO_GLOBAL.add(Settings.Global.WEB_AUTOFILL_QUERY_URL); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_COUNTRY_CODE); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_FREQUENCY_BAND); @@ -2838,6 +2837,8 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SAVED_STATE); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED); + MOVED_TO_GLOBAL.add(Settings.Global.WIFI_ENHANCED_AUTO_JOIN); + MOVED_TO_GLOBAL.add(Settings.Global.WIFI_NETWORK_SHOW_RSSI); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_ON); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON); @@ -3480,6 +3481,12 @@ public final class Settings { "lock_screen_appwidget_ids"; /** + * List of enrolled fingerprint identifiers (comma-delimited). + * @hide + */ + public static final String USER_FINGERPRINT_IDS = "user_fingerprint_ids"; + + /** * Id of the appwidget shown on the lock screen when appwidgets are disabled. * @hide */ @@ -5347,11 +5354,6 @@ public final class Settings { */ public static final String USE_GOOGLE_MAIL = "use_google_mail"; - /** Autofill server address (Used in WebView/browser). - * {@hide} */ - public static final String WEB_AUTOFILL_QUERY_URL = - "web_autofill_query_url"; - /** * Whether Wifi display is enabled/disabled * 0=disabled. 1=enabled. @@ -5463,7 +5465,21 @@ public final class Settings { public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS = "wifi_supplicant_scan_interval_ms"; - /** + /** + * whether frameworks handles wifi auto-join + * @hide + */ + public static final String WIFI_ENHANCED_AUTO_JOIN = + "wifi_enhanced_auto_join"; + + /** + * whether settings show RSSI + * @hide + */ + public static final String WIFI_NETWORK_SHOW_RSSI = + "wifi_network_show_rssi"; + + /** * The interval in milliseconds to scan at supplicant when p2p is connected * @hide */ diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java new file mode 100644 index 0000000..0d14c59 --- /dev/null +++ b/core/java/android/service/fingerprint/FingerprintManager.java @@ -0,0 +1,200 @@ +/** + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.fingerprint; + +import android.app.ActivityManagerNative; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; + +/** + * A class that coordinates access to the fingerprint hardware. + */ + +public class FingerprintManager { + private static final String TAG = "FingerprintManager"; + protected static final boolean DEBUG = true; + private static final String FINGERPRINT_SERVICE_PACKAGE = "com.android.service.fingerprint"; + private static final String FINGERPRINT_SERVICE_CLASS = + "com.android.service.fingerprint.FingerprintService"; + private static final int MSG_ENROLL_RESULT = 100; + private static final int MSG_SCANNED = 101; + private static final int MSG_ERROR = 102; + private static final int MSG_REMOVED = 103; + + public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; + public static final int FINGERPRINT_ERROR = -1; // One of the error messages below. + + // Progress messages. + public static final int FINGERPRINT_SCANNED = 1; + public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; + public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; + + // Error messages. Must agree with fingerprint HAL definitions. + public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; + public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; + public static final int FINGERPRINT_ERROR_TIMEOUT = 3; + public static final int FINGERPRINT_ERROR_NO_SPACE = 4; + + private IFingerprintService mService; + private FingerprintManagerReceiver mClientReceiver; + + private Handler mHandler = new Handler() { + public void handleMessage(android.os.Message msg) { + if (mClientReceiver != null) { + switch(msg.what) { + case MSG_ENROLL_RESULT: + mClientReceiver.onEnrollResult(msg.arg1, msg.arg2); + break; + case MSG_SCANNED: + mClientReceiver.onScanned(msg.arg1, msg.arg2); + break; + case MSG_ERROR: + mClientReceiver.onError(msg.arg1); + break; + case MSG_REMOVED: + mClientReceiver.onRemoved(msg.arg1); + } + } + } + }; + + public FingerprintManager(Context context) { + // Connect to service... + Intent intent = new Intent(); + intent.setClassName(FINGERPRINT_SERVICE_PACKAGE, FINGERPRINT_SERVICE_CLASS); + if (!context.bindServiceAsUser(intent, mFingerprintConnection, + Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF)) { + if (DEBUG) Log.v(TAG, "Can't bind to " + FINGERPRINT_SERVICE_CLASS); + } + } + + private final ServiceConnection mFingerprintConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Log.v(TAG, "Connected to FingerprintService"); + mService = IFingerprintService.Stub.asInterface(service); + try { + mService.startListening(mServiceReceiver, getCurrentUserId()); + } catch (RemoteException e) { + if (DEBUG) Log.v(TAG, "Failed to set callback", e); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.v(TAG, "Disconnected from FingerprintService"); + mService = null; + } + }; + + private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() { + + public void onEnrollResult(int fingerprintId, int remaining) { + mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget(); + } + + public void onScanned(int fingerprintId, int confidence) { + mHandler.obtainMessage(MSG_SCANNED, fingerprintId, confidence) + .sendToTarget();; + } + + public void onError(int error) { + mHandler.obtainMessage(MSG_ERROR, error, 0).sendToTarget(); + } + + public void onRemoved(int fingerprintId) { + mHandler.obtainMessage(MSG_REMOVED, fingerprintId, 0).sendToTarget(); + } + }; + + /** + * Start the enrollment process. Timeout dictates how long to wait for the user to + * enroll a fingerprint. + * + * @param timeout + */ + public void enroll(long timeout) { + if (mServiceReceiver == null) { + throw new IllegalStateException("enroll: Call registerCallback() first"); + } + if (mService != null) try { + mService.enroll(timeout, getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception while enrolling: ", e); + } + } + + /** + * Remove the given fingerprintId from the system. FingerprintId of 0 has special meaning + * which is to delete all fingerprint data for the current user. Use with caution. + * @param fingerprintId + */ + public void remove(int fingerprintId) { + if (mService != null) try { + mService.remove(fingerprintId, getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e); + } + } + + /** + * Starts listening for fingerprint events. When a finger is scanned or recognized, the + * client will be notified via the callback. + */ + public void startListening(FingerprintManagerReceiver receiver) { + mClientReceiver = receiver; + if (mService != null) { + try { + mService.startListening(mServiceReceiver, getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception in startListening(): ", e); + } + } + } + + private int getCurrentUserId() { + try { + return ActivityManagerNative.getDefault().getCurrentUser().id; + } catch (RemoteException e) { + Log.w(TAG, "Failed to get current user id\n"); + return UserHandle.USER_NULL; + } + } + + /** + * Stops the client from listening to fingerprint events. + */ + public void stopListening() { + mClientReceiver = null; + if (mService != null) { + try { + mService.stopListening(getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception in stopListening(): ", e); + } + } else { + Log.w(TAG, "stopListening(): Service not connected!"); + } + } +}
\ No newline at end of file diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java new file mode 100644 index 0000000..34f1655 --- /dev/null +++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java @@ -0,0 +1,59 @@ +package android.service.fingerprint; +/** + * 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. + */ + +public class FingerprintManagerReceiver { + /** + * Fingerprint enrollment progress update. Enrollment is considered complete if + * remaining hits 0 without {@link #onError(int)} being called. + * + * @param fingerprintId the fingerprint we're currently enrolling + * @param remaining the number of samples required to complete enrollment. It's up to + * the hardware to define what each step in enrollment means. Some hardware + * requires multiple samples of the same part of the finger. Others require sampling of + * different parts of the finger. The enrollment flow can use remaining to + * mean "step x" of the process or "just need another sample." + */ + public void onEnrollResult(int fingerprintId, int remaining) { } + + /** + * Fingerprint scan detected. Most clients will use this function to detect a fingerprint + * + * @param fingerprintId is the finger the hardware has detected. + * @param confidence from 0 (no confidence) to 65535 (high confidence). Fingerprint 0 has + * special meaning - the finger wasn't recognized. + */ + public void onScanned(int fingerprintId, int confidence) { } + + /** + * An error was detected during scan or enrollment. One of + * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}, + * {@link FingerprintManager#FINGERPRINT_ERROR_BAD_CAPTURE} or + * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT} + * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE} + * + * @param error one of the above error codes + */ + public void onError(int error) { } + + /** + * The given fingerprint template was successfully removed by the driver. + * See {@link FingerprintManager#remove(int)} + * + * @param fingerprintId id of template to remove. + */ + public void onRemoved(int fingerprintId) { } +}
\ No newline at end of file diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java new file mode 100644 index 0000000..c7fa7cd --- /dev/null +++ b/core/java/android/service/fingerprint/FingerprintService.java @@ -0,0 +1,219 @@ +/** + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.fingerprint; + +import android.app.Service; +import android.content.ContentResolver; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.provider.Settings; +import android.util.Slog; + +import java.io.PrintWriter; +import java.util.HashMap; + +/** + * A service to manage multiple clients that want to access the fingerprint HAL API. + * The service is responsible for maintaining a list of clients and dispatching all + * fingerprint -related events. + * + * @hide + */ +public class FingerprintService extends Service { + private final String TAG = FingerprintService.class.getSimpleName() + + "[" + getClass().getSimpleName() + "]"; + private static final boolean DEBUG = true; + HashMap<IFingerprintServiceReceiver, ClientData> mClients = + new HashMap<IFingerprintServiceReceiver, ClientData>(); + + private static final int MSG_NOTIFY = 10; + + Handler mHandler = new Handler() { + public void handleMessage(android.os.Message msg) { + switch (msg.what) { + case MSG_NOTIFY: + handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj); + break; + + default: + Slog.w(TAG, "Unknown message:" + msg.what); + } + } + }; + + private static final int STATE_IDLE = 0; + private static final int STATE_LISTENING = 1; + private static final int STATE_ENROLLING = 2; + private static final int STATE_DELETING = 3; + private static final long MS_PER_SEC = 1000; + + private static final class ClientData { + public IFingerprintServiceReceiver receiver; + int state; + int userId; + } + + @Override + public final IBinder onBind(Intent intent) { + if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent); + return new FingerprintServiceWrapper(); + } + + // JNI methods to communicate from FingerprintManagerService to HAL + native int nativeEnroll(int timeout); + native int nativeRemove(int fingerprintId); + + // JNI methods for communicating from HAL to clients + void notify(int msg, int arg1, int arg2) { + mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget(); + } + + void handleNotify(int msg, int arg1, int arg2) { + for (int i = 0; i < mClients.size(); i++) { + ClientData clientData = mClients.get(i); + switch (msg) { + case FingerprintManager.FINGERPRINT_ERROR: { + if (clientData.state != STATE_IDLE) { + // FINGERPRINT_ERROR_HW_UNAVAILABLE + // FINGERPRINT_ERROR_BAD_CAPTURE + // FINGERPRINT_ERROR_TIMEOUT + // FINGERPRINT_ERROR_NO_SPACE + final int error = arg1; + clientData.state = STATE_IDLE; + if (clientData.receiver != null) { + try { + clientData.receiver.onError(error); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + } + } + } + } + break; + case FingerprintManager.FINGERPRINT_SCANNED: { + final int fingerId = arg1; + final int confidence = arg2; + if (clientData.state == STATE_LISTENING && clientData.receiver != null) { + try { + clientData.receiver.onScanned(fingerId, confidence); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + } + } + break; + } + case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: { + if (clientData.state == STATE_ENROLLING) { + final int fingerId = arg1; + final int remaining = arg2; + if (remaining == 0) { + FingerprintUtils.addFingerprintIdForUser(fingerId, + getContentResolver(), clientData.userId); + clientData.state = STATE_IDLE; // Nothing left to do + } + if (clientData.receiver != null) { + try { + clientData.receiver.onEnrollResult(fingerId, remaining); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + } + } + } + break; + } + case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: { + int fingerId = arg1; + if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL"); + if (clientData.state == STATE_DELETING) { + FingerprintUtils.removeFingerprintIdForUser(fingerId, getContentResolver(), + clientData.userId); + if (clientData.receiver != null) { + try { + clientData.receiver.onRemoved(fingerId); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + } + } + } + } + break; + } + } + } + + int enroll(IFingerprintServiceReceiver receiver, long timeout, int userId) { + ClientData clientData = mClients.get(receiver); + if (clientData != null) { + if (clientData.userId != userId) throw new IllegalStateException("Bad user"); + clientData.state = STATE_ENROLLING; + return nativeEnroll((int) (timeout / MS_PER_SEC)); + } + return -1; + } + + int remove(IFingerprintServiceReceiver receiver, int fingerId, int userId) { + ClientData clientData = mClients.get(receiver); + if (clientData != null) { + if (clientData.userId != userId) throw new IllegalStateException("Bad user"); + clientData.state = STATE_DELETING; + // The fingerprint id will be removed when we get confirmation from the HAL + return nativeRemove(fingerId); + } + return -1; + } + + void startListening(IFingerprintServiceReceiver receiver, int userId) { + ClientData clientData = new ClientData(); + clientData.state = STATE_LISTENING; + clientData.receiver = receiver; + clientData.userId = userId; + mClients.put(receiver, clientData); + } + + void stopListening(IFingerprintServiceReceiver receiver, int userId) { + ClientData clientData = mClients.get(receiver); + if (clientData != null) { + clientData.state = STATE_IDLE; + clientData.userId = -1; + clientData.receiver = null; + } + mClients.remove(receiver); + } + + private final class FingerprintServiceWrapper extends IFingerprintService.Stub { + IFingerprintServiceReceiver mReceiver; + public int enroll(long timeout, int userId) { + return mReceiver != null ? FingerprintService.this.enroll(mReceiver, timeout, userId) + : FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER; + } + + public int remove(int fingerprintId, int userId) { + return FingerprintService.this.remove(mReceiver, fingerprintId, userId); + } + + public void startListening(IFingerprintServiceReceiver receiver, int userId) { + mReceiver = receiver; + FingerprintService.this.startListening(receiver, userId); + } + + public void stopListening(int userId) { + FingerprintService.this.stopListening(mReceiver, userId); + } + } +} diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java new file mode 100644 index 0000000..81a2aac --- /dev/null +++ b/core/java/android/service/fingerprint/FingerprintUtils.java @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.fingerprint; + +import android.content.ContentResolver; +import android.provider.Settings; +import android.util.Log; + +import java.util.Arrays; + +class FingerprintUtils { + private static final boolean DEBUG = true; + private static final String TAG = "FingerprintUtils"; + + public static int[] getFingerprintIdsForUser(ContentResolver res, int userId) { + String fingerIdsRaw = Settings.Secure.getStringForUser(res, + Settings.Secure.USER_FINGERPRINT_IDS, userId); + + String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", "); + int result[] = new int[fingerStringIds.length]; + for (int i = 0; i < result.length; i++) { + try { + result[i] = Integer.decode(fingerStringIds[i]); + } catch (NumberFormatException e) { + if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]); + } + } + return result; + } + + public static void addFingerprintIdForUser(int fingerId, ContentResolver res, int userId) { + int[] fingerIds = getFingerprintIdsForUser(res, userId); + + // FingerId 0 has special meaning. + if (fingerId == 0) return; + + // Don't allow dups + for (int i = 0; i < fingerIds.length; i++) { + if (fingerIds[i] == fingerId) return; + } + int[] newList = Arrays.copyOf(fingerIds, fingerIds.length + 1); + newList[fingerIds.length] = fingerId; + Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, + Arrays.toString(newList), userId); + } + + public static boolean removeFingerprintIdForUser(int fingerId, ContentResolver res, int userId) + { + // FingerId 0 has special meaning. The HAL layer is supposed to remove each finger one + // at a time and invoke notify() for each fingerId. If we get called with 0 here, it means + // something bad has happened. + if (fingerId == 0) throw new IllegalStateException("Bad fingerId"); + + int[] fingerIds = getFingerprintIdsForUser(res, userId); + int[] resultIds = Arrays.copyOf(fingerIds, fingerIds.length); + int resultCount = 0; + for (int i = 0; i < fingerIds.length; i++) { + if (fingerId != fingerIds[i]) { + resultIds[resultCount++] = fingerIds[i]; + } + } + if (resultCount > 0) { + Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, + Arrays.toString(Arrays.copyOf(resultIds, resultCount)), userId); + return true; + } + return false; + } + +}; + diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl new file mode 100644 index 0000000..e92c20c --- /dev/null +++ b/core/java/android/service/fingerprint/IFingerprintService.aidl @@ -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 android.service.fingerprint; + +import android.os.Bundle; +import android.service.fingerprint.IFingerprintServiceReceiver; + +/** + * Communication channel from client to the fingerprint service. + * @hide + */ +interface IFingerprintService { + // Returns 0 if successfully started, -1 otherwise + int enroll(long timeout, int userId); + + // Returns 0 if fingerprintId's template can be removed, -1 otherwise + int remove(int fingerprintId, int userId); + + // Start listening for fingerprint events. This has the side effect of starting + // the hardware if not already started. + oneway void startListening(IFingerprintServiceReceiver receiver, int userId); + + // Stops listening for fingerprints + oneway void stopListening(int userId); +} diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl new file mode 100644 index 0000000..4826b59 --- /dev/null +++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.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.service.fingerprint; + +import android.os.Bundle; +import android.os.UserHandle; + +/** + * Communication channel from the FingerprintService back to FingerprintManager. + * @hide + */ +oneway interface IFingerprintServiceReceiver { + void onEnrollResult(int fingerprintId, int remaining); + void onScanned(int fingerprintId, int confidence); + void onError(int error); + void onRemoved(int fingerprintId); +} diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 2549fde..49a0138 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -351,18 +351,8 @@ public abstract class Transition implements Cloneable { } ArrayMap<View, TransitionValues> endCopy = new ArrayMap<View, TransitionValues>(endValues.viewValues); - SparseArray<TransitionValues> endIdCopy = - new SparseArray<TransitionValues>(endValues.idValues.size()); - for (int i = 0; i < endValues.idValues.size(); ++i) { - int id = endValues.idValues.keyAt(i); - endIdCopy.put(id, endValues.idValues.valueAt(i)); - } - LongSparseArray<TransitionValues> endItemIdCopy = - new LongSparseArray<TransitionValues>(endValues.itemIdValues.size()); - for (int i = 0; i < endValues.itemIdValues.size(); ++i) { - long id = endValues.itemIdValues.keyAt(i); - endItemIdCopy.put(id, endValues.itemIdValues.valueAt(i)); - } + SparseArray<TransitionValues> endIdCopy = endValues.idValues.clone(); + LongSparseArray<TransitionValues> endItemIdCopy = endValues.itemIdValues.clone(); // Walk through the start values, playing everything we find // Remove from the end set as we go ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>(); @@ -376,21 +366,17 @@ public abstract class Transition implements Cloneable { } if (!isInListView) { int id = view.getId(); - start = startValues.viewValues.get(view) != null ? - startValues.viewValues.get(view) : startValues.idValues.get(id); - if (endValues.viewValues.get(view) != null) { - end = endValues.viewValues.get(view); + start = startValues.viewValues.get(view); + end = endValues.viewValues.get(view); + if (end != null) { endCopy.remove(view); } else if (id != View.NO_ID) { - end = endValues.idValues.get(id); - View removeView = null; - for (View viewToRemove : endCopy.keySet()) { - if (viewToRemove.getId() == id) { - removeView = viewToRemove; - } - } - if (removeView != null) { - endCopy.remove(removeView); + end = endIdCopy.get(id); + if (end == null || startValues.viewValues.containsKey(end.view)) { + end = null; + id = View.NO_ID; + } else { + endCopy.remove(end.view); } } endIdCopy.remove(id); @@ -423,36 +409,16 @@ public abstract class Transition implements Cloneable { } } // Now walk through the remains of the end set + // We've already matched everything from start to end, everything else doesn't match. for (View view : endCopy.keySet()) { int id = view.getId(); if (isValidTarget(view, id)) { - TransitionValues start = startValues.viewValues.get(view) != null ? - startValues.viewValues.get(view) : startValues.idValues.get(id); + TransitionValues start = null; TransitionValues end = endCopy.get(view); - endIdCopy.remove(id); startValuesList.add(start); endValuesList.add(end); } } - int endIdCopySize = endIdCopy.size(); - for (int i = 0; i < endIdCopySize; ++i) { - int id = endIdCopy.keyAt(i); - if (isValidTarget(null, id)) { - TransitionValues start = startValues.idValues.get(id); - TransitionValues end = endIdCopy.get(id); - startValuesList.add(start); - endValuesList.add(end); - } - } - int endItemIdCopySize = endItemIdCopy.size(); - for (int i = 0; i < endItemIdCopySize; ++i) { - long id = endItemIdCopy.keyAt(i); - // TODO: Deal with targetIDs and itemIDs - TransitionValues start = startValues.itemIdValues.get(id); - TransitionValues end = endItemIdCopy.get(id); - startValuesList.add(start); - endValuesList.add(end); - } ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators(); long minStartDelay = Long.MAX_VALUE; int minAnimator = mAnimators.size(); diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 11948b2..6c451eb 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -43,7 +43,6 @@ import android.text.TextUtils; class GLES20Canvas extends HardwareCanvas { // Must match modifiers used in the JNI layer private static final int MODIFIER_NONE = 0; - private static final int MODIFIER_SHADOW = 1; private static final int MODIFIER_SHADER = 2; private final boolean mOpaque; @@ -264,27 +263,6 @@ class GLES20Canvas extends HardwareCanvas { private static native int nCallDrawGLFunction(long renderer, long drawGLFunction); - @Override - public int invokeFunctors(Rect dirty) { - return nInvokeFunctors(mRenderer, dirty); - } - - private static native int nInvokeFunctors(long renderer, Rect dirty); - - @Override - public void detachFunctor(long functor) { - nDetachFunctor(mRenderer, functor); - } - - private static native void nDetachFunctor(long renderer, long functor); - - @Override - public void attachFunctor(long functor) { - nAttachFunctor(mRenderer, functor); - } - - private static native void nAttachFunctor(long renderer, long functor); - /////////////////////////////////////////////////////////////////////////// // Memory /////////////////////////////////////////////////////////////////////////// @@ -1318,12 +1296,6 @@ class GLES20Canvas extends HardwareCanvas { private int setupModifiers(Paint paint) { int modifiers = MODIFIER_NONE; - if (paint.hasShadow) { - nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy, - paint.shadowColor); - modifiers |= MODIFIER_SHADOW; - } - final Shader shader = paint.getShader(); if (shader != null) { nSetupShader(mRenderer, shader.native_shader); @@ -1336,12 +1308,6 @@ class GLES20Canvas extends HardwareCanvas { private int setupModifiers(Paint paint, int flags) { int modifiers = MODIFIER_NONE; - if (paint.hasShadow && (flags & MODIFIER_SHADOW) != 0) { - nSetupShadow(mRenderer, paint.shadowRadius, paint.shadowDx, paint.shadowDy, - paint.shadowColor); - modifiers |= MODIFIER_SHADOW; - } - final Shader shader = paint.getShader(); if (shader != null && (flags & MODIFIER_SHADER) != 0) { nSetupShader(mRenderer, shader.native_shader); @@ -1352,8 +1318,6 @@ class GLES20Canvas extends HardwareCanvas { } private static native void nSetupShader(long renderer, long shader); - private static native void nSetupShadow(long renderer, float radius, - float dx, float dy, int color); private static native void nResetModifiers(long renderer, int modifiers); } diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java index 97339cc..7b49006 100644 --- a/core/java/android/view/GLRenderer.java +++ b/core/java/android/view/GLRenderer.java @@ -168,7 +168,6 @@ public class GLRenderer extends HardwareRenderer { private final Rect mRedrawClip = new Rect(); private final int[] mSurfaceSize = new int[2]; - private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable(); private long mDrawDelta = Long.MAX_VALUE; @@ -654,6 +653,11 @@ public class GLRenderer extends HardwareRenderer { } @Override + void setOpaque(boolean opaque) { + // Not supported + } + + @Override boolean loadSystemProperties() { boolean value; boolean changed = false; @@ -1116,22 +1120,6 @@ public class GLRenderer extends HardwareRenderer { mName = name; } - class FunctorsRunnable implements Runnable { - View.AttachInfo attachInfo; - - @Override - public void run() { - final HardwareRenderer renderer = attachInfo.mHardwareRenderer; - if (renderer == null || !renderer.isEnabled() || renderer != GLRenderer.this) { - return; - } - - if (checkRenderContext() != SURFACE_STATE_ERROR) { - mCanvas.invokeFunctors(mRedrawClip); - } - } - } - @Override void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { @@ -1366,23 +1354,6 @@ public class GLRenderer extends HardwareRenderer { } } - @Override - void detachFunctor(long functor) { - if (mCanvas != null) { - mCanvas.detachFunctor(functor); - } - } - - @Override - void attachFunctor(View.AttachInfo attachInfo, long functor) { - if (mCanvas != null) { - mCanvas.attachFunctor(functor); - mFunctorsRunnable.attachInfo = attachInfo; - attachInfo.mHandler.removeCallbacks(mFunctorsRunnable); - attachInfo.mHandler.postDelayed(mFunctorsRunnable, 0); - } - } - /** * Ensures the current EGL context and surface are the ones we expect. * This method throws an IllegalStateException if invoked from a thread diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index 7ec2cc6..9568760 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -111,45 +111,6 @@ public abstract class HardwareCanvas extends Canvas { } /** - * Invoke all the functors who requested to be invoked during the previous frame. - * - * @param dirty Ignored - * - * @return Ignored - * - * @hide - */ - public int invokeFunctors(Rect dirty) { - return RenderNode.STATUS_DONE; - } - - /** - * Detaches the specified functor from the current functor execution queue. - * - * @param functor The native functor to remove from the execution queue. - * - * @see #invokeFunctors(android.graphics.Rect) - * @see #callDrawGLFunction(long) - * @see #detachFunctor(long) - * - * @hide - */ - abstract void detachFunctor(long functor); - - /** - * Attaches the specified functor to the current functor execution queue. - * - * @param functor The native functor to add to the execution queue. - * - * @see #invokeFunctors(android.graphics.Rect) - * @see #callDrawGLFunction(long) - * @see #detachFunctor(long) - * - * @hide - */ - abstract void attachFunctor(long functor); - - /** * Indicates that the specified layer must be updated as soon as possible. * * @param layer The layer to update diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index d31c79d..e366697 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -423,28 +423,6 @@ public abstract class HardwareRenderer { abstract boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap); /** - * Detaches the specified functor from the current functor execution queue. - * - * @param functor The native functor to remove from the execution queue. - * - * @see HardwareCanvas#callDrawGLFunction(int) - * @see #attachFunctor(android.view.View.AttachInfo, long) - */ - abstract void detachFunctor(long functor); - - /** - * Schedules the specified functor in the functors execution queue. - * - * @param attachInfo AttachInfo tied to this renderer. - * @param functor The native functor to insert in the execution queue. - * - * @see HardwareCanvas#callDrawGLFunction(int) - * @see #detachFunctor(long) - * - */ - abstract void attachFunctor(View.AttachInfo attachInfo, long functor); - - /** * Schedules the functor for execution in either kModeProcess or * kModeProcessNoContext, depending on whether or not there is an EGLContext. * @@ -491,6 +469,11 @@ public abstract class HardwareRenderer { abstract void setName(String name); /** + * Change the HardwareRenderer's opacity + */ + abstract void setOpaque(boolean opaque); + + /** * Creates a hardware renderer using OpenGL. * * @param translucent True if the surface is translucent, false otherwise diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 3cfe5e9..1765c43 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -23,7 +23,6 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.os.Looper; import android.util.AttributeSet; import android.util.Log; @@ -119,8 +118,6 @@ public class TextureView extends View { private boolean mUpdateLayer; private boolean mUpdateSurface; - private SurfaceTexture.OnFrameAvailableListener mUpdateListener; - private Canvas mCanvas; private int mSaveCount; @@ -370,21 +367,7 @@ public class TextureView extends View { mSurface.setDefaultBufferSize(getWidth(), getHeight()); nCreateNativeWindow(mSurface); - mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() { - @Override - public void onFrameAvailable(SurfaceTexture surfaceTexture) { - // Per SurfaceTexture's documentation, the callback may be invoked - // from an arbitrary thread - updateLayer(); - - if (Looper.myLooper() == Looper.getMainLooper()) { - invalidate(); - } else { - postInvalidate(); - } - } - }; - mSurface.setOnFrameAvailableListener(mUpdateListener); + mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); if (mListener != null && !mUpdateSurface) { mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); @@ -422,7 +405,7 @@ public class TextureView extends View { // To cancel updates, the easiest thing to do is simply to remove the // updates listener if (visibility == VISIBLE) { - mSurface.setOnFrameAvailableListener(mUpdateListener); + mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); updateLayerAndInvalidate(); } else { mSurface.setOnFrameAvailableListener(null); @@ -767,6 +750,15 @@ public class TextureView extends View { mListener = listener; } + private final SurfaceTexture.OnFrameAvailableListener mUpdateListener = + new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + updateLayer(); + invalidate(); + } + }; + /** * This listener can be used to be notified when the surface texture * associated with this texture view is available. diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 0bf99d3..2587ba1 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -148,6 +148,11 @@ public class ThreadedRenderer extends HardwareRenderer { } @Override + void setOpaque(boolean opaque) { + nSetOpaque(mNativeProxy, opaque); + } + + @Override int getWidth() { return mWidth; } @@ -215,16 +220,6 @@ public class ThreadedRenderer extends HardwareRenderer { } @Override - void detachFunctor(long functor) { - // no-op, we never attach functors to need to detach them - } - - @Override - void attachFunctor(AttachInfo attachInfo, long functor) { - invokeFunctor(functor, true); - } - - @Override void invokeFunctor(long functor, boolean waitForCompletion) { nInvokeFunctor(mNativeProxy, functor, waitForCompletion); } @@ -312,6 +307,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native void nUpdateSurface(long nativeProxy, Surface window); private static native void nPauseSurface(long nativeProxy, Surface window); private static native void nSetup(long nativeProxy, int width, int height); + private static native void nSetOpaque(long nativeProxy, boolean opaque); private static native void nSetDisplayListData(long nativeProxy, long displayList, long newData); private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos, diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3998c04..bef96b1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -16,7 +16,9 @@ package android.view; +import android.animation.AnimatorInflater; import android.animation.RevealAnimator; +import android.animation.StateListAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; @@ -667,6 +669,7 @@ import java.util.concurrent.atomic.AtomicInteger; * @attr ref android.R.styleable#View_scrollbarTrackVertical * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack + * @attr ref android.R.styleable#View_stateListAnimator * @attr ref android.R.styleable#View_sharedElementName * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag @@ -3258,6 +3261,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private Outline mOutline; /** + * Animator that automatically runs based on state changes. + */ + private StateListAnimator mStateListAnimator; + + /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, * the user may specify which view to go to next. */ @@ -3995,6 +4003,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case R.styleable.View_nestedScrollingEnabled: setNestedScrollingEnabled(a.getBoolean(attr, false)); break; + case R.styleable.View_stateListAnimator: + setStateListAnimator(AnimatorInflater.loadStateListAnimator(context, + a.getResourceId(attr, 0))); + break; } } @@ -6147,12 +6159,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // call into it as a fallback in case we're in a class that overrides it // and has logic to perform. if (fitSystemWindows(insets.getSystemWindowInsets())) { - return insets.cloneWithSystemWindowInsetsConsumed(); + return insets.consumeSystemWindowInsets(); } } else { // We were called from within a direct call to fitSystemWindows. if (fitSystemWindowsInt(insets.getSystemWindowInsets())) { - return insets.cloneWithSystemWindowInsetsConsumed(); + return insets.consumeSystemWindowInsets(); } } return insets; @@ -9766,6 +9778,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9809,6 +9822,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9852,6 +9866,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9887,6 +9902,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9922,6 +9938,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10083,6 +10100,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_ALPHA_SET; invalidateViewProperty(true, false); mRenderNode.setAlpha(getFinalAlpha()); + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } @@ -10513,6 +10532,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10612,6 +10632,40 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Returns the current StateListAnimator if exists. + * + * @return StateListAnimator or null if it does not exists + * @see #setStateListAnimator(android.animation.StateListAnimator) + */ + public StateListAnimator getStateListAnimator() { + return mStateListAnimator; + } + + /** + * Attaches the provided StateListAnimator to this View. + * <p> + * Any previously attached StateListAnimator will be detached. + * + * @param stateListAnimator The StateListAnimator to update the view + * @see {@link android.animation.StateListAnimator} + */ + public void setStateListAnimator(StateListAnimator stateListAnimator) { + if (mStateListAnimator == stateListAnimator) { + return; + } + if (mStateListAnimator != null) { + mStateListAnimator.setTarget(null); + } + mStateListAnimator = stateListAnimator; + if (stateListAnimator != null) { + stateListAnimator.setTarget(this); + if (isAttachedToWindow()) { + stateListAnimator.setState(getDrawableState()); + } + } + } + + /** * Sets the outline of the view, which defines the shape of the shadow it * casts. * <p> @@ -10661,6 +10715,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { mRenderNode.setOutline(null); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10815,6 +10870,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10861,6 +10917,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -12824,7 +12881,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, destroyLayer(false); cleanupDraw(); - mCurrentAnimation = null; } @@ -15478,9 +15534,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * This function is called whenever the state of the view changes in such * a way that it impacts the state of drawables being shown. - * - * <p>Be sure to call through to the superclass when overriding this - * function. + * <p> + * If the View has a StateListAnimator, it will also be called to run necessary state + * change animations. + * <p> + * Be sure to call through to the superclass when overriding this function. * * @see Drawable#setState(int[]) */ @@ -15489,6 +15547,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (d != null && d.isStateful()) { d.setState(getDrawableState()); } + + if (mStateListAnimator != null) { + mStateListAnimator.setState(getDrawableState()); + } } /** @@ -15633,11 +15695,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()} * on all Drawable objects associated with this view. + * <p> + * Also calls {@link StateListAnimator#jumpToCurrentState()} if there is a StateListAnimator + * attached to this view. */ public void jumpDrawablesToCurrentState() { if (mBackground != null) { mBackground.jumpToCurrentState(); } + if (mStateListAnimator != null) { + mStateListAnimator.jumpToCurrentState(); + } } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 43bc0b6..4309366 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4638,6 +4638,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (invalidate) { invalidateViewProperty(false, false); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index db87394..9b09d85 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -665,18 +665,9 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES)); } - public void attachFunctor(long functor) { - //noinspection SimplifiableIfStatement - if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { - mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor); - } - } - public void detachFunctor(long functor) { + // TODO: Make the resize buffer some other way to not need this block mBlockResizeBuffer = true; - if (mAttachInfo.mHardwareRenderer != null) { - mAttachInfo.mHardwareRenderer.detachFunctor(functor); - } } public boolean invokeFunctor(long functor, boolean waitForCompletion) { @@ -719,12 +710,15 @@ public final class ViewRootImpl implements ViewParent, if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) { - // Don't enable hardware acceleration when we're not on the main thread - if (!HardwareRenderer.sSystemRendererDisabled && - Looper.getMainLooper() != Looper.myLooper()) { - Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " - + "acceleration outside of the main thread, aborting"); - return; + if (!HardwareRenderer.sUseRenderThread) { + // TODO: Delete + // Don't enable hardware acceleration when we're not on the main thread + if (!HardwareRenderer.sSystemRendererDisabled && + Looper.getMainLooper() != Looper.myLooper()) { + Log.w(HardwareRenderer.LOG_TAG, "Attempting to initialize hardware " + + "acceleration outside of the main thread, aborting"); + return; + } } if (mAttachInfo.mHardwareRenderer != null) { @@ -6174,8 +6168,10 @@ public final class ViewRootImpl implements ViewParent, } void changeCanvasOpacity(boolean opaque) { - // TODO(romainguy): recreate Canvas (software or hardware) to reflect the opacity change. Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque); + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.setOpaque(opaque); + } } class TakenSurfaceHolder extends BaseSurfaceHolder { diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 2160efe..294f472 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -29,7 +29,7 @@ import android.graphics.Rect; * @see View.OnApplyWindowInsetsListener * @see View#onApplyWindowInsets(WindowInsets) */ -public class WindowInsets { +public final class WindowInsets { private Rect mSystemWindowInsets; private Rect mWindowDecorInsets; private Rect mTempRect; @@ -151,6 +151,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return The left window decor inset + * @hide pending API */ public int getWindowDecorInsetLeft() { return mWindowDecorInsets.left; @@ -164,6 +165,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return The top window decor inset + * @hide pending API */ public int getWindowDecorInsetTop() { return mWindowDecorInsets.top; @@ -177,6 +179,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return The right window decor inset + * @hide pending API */ public int getWindowDecorInsetRight() { return mWindowDecorInsets.right; @@ -190,6 +193,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return The bottom window decor inset + * @hide pending API */ public int getWindowDecorInsetBottom() { return mWindowDecorInsets.bottom; @@ -217,6 +221,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return true if any of the window decor inset values are nonzero + * @hide pending API */ public boolean hasWindowDecorInsets() { return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 || @@ -246,13 +251,28 @@ public class WindowInsets { return mIsRound; } - public WindowInsets cloneWithSystemWindowInsetsConsumed() { + /** + * Returns a copy of this WindowInsets with the system window insets fully consumed. + * + * @return A modified copy of this WindowInsets + */ + public WindowInsets consumeSystemWindowInsets() { final WindowInsets result = new WindowInsets(this); result.mSystemWindowInsets = new Rect(0, 0, 0, 0); return result; } - public WindowInsets cloneWithSystemWindowInsetsConsumed(boolean left, boolean top, + /** + * Returns a copy of this WindowInsets with selected system window insets fully consumed. + * + * @param left true to consume the left system window inset + * @param top true to consume the top system window inset + * @param right true to consume the right system window inset + * @param bottom true to consume the bottom system window inset + * @return A modified copy of this WindowInsets + * @hide pending API + */ + public WindowInsets consumeSystemWindowInsets(boolean left, boolean top, boolean right, boolean bottom) { if (left || top || right || bottom) { final WindowInsets result = new WindowInsets(this); @@ -265,19 +285,36 @@ public class WindowInsets { return this; } - public WindowInsets cloneWithSystemWindowInsets(int left, int top, int right, int bottom) { + /** + * Returns a copy of this WindowInsets with selected system window insets replaced + * with new values. + * + * @param left New left inset in pixels + * @param top New top inset in pixels + * @param right New right inset in pixels + * @param bottom New bottom inset in pixels + * @return A modified copy of this WindowInsets + */ + public WindowInsets replaceSystemWindowInsets(int left, int top, + int right, int bottom) { final WindowInsets result = new WindowInsets(this); result.mSystemWindowInsets = new Rect(left, top, right, bottom); return result; } - public WindowInsets cloneWithWindowDecorInsetsConsumed() { + /** + * @hide + */ + public WindowInsets consumeWindowDecorInsets() { final WindowInsets result = new WindowInsets(this); result.mWindowDecorInsets.set(0, 0, 0, 0); return result; } - public WindowInsets cloneWithWindowDecorInsetsConsumed(boolean left, boolean top, + /** + * @hide + */ + public WindowInsets consumeWindowDecorInsets(boolean left, boolean top, boolean right, boolean bottom) { if (left || top || right || bottom) { final WindowInsets result = new WindowInsets(this); @@ -290,7 +327,10 @@ public class WindowInsets { return this; } - public WindowInsets cloneWithWindowDecorInsets(int left, int top, int right, int bottom) { + /** + * @hide + */ + public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) { final WindowInsets result = new WindowInsets(this); result.mWindowDecorInsets = new Rect(left, top, right, bottom); return result; diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java index 2f8850b..3e33498 100644 --- a/core/java/android/webkit/PermissionRequest.java +++ b/core/java/android/webkit/PermissionRequest.java @@ -19,14 +19,11 @@ package android.webkit; import android.net.Uri; /** - * This class wraps a permission request, and is used to request permission for - * the web content to access the resources. + * This interface defines a permission request and is used when web content + * requests access to protected resources. * - * Either {@link #grant(long) grant()} or {@link #deny()} must be called to response the - * request, otherwise, {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will - * not be invoked again if there is other permission request in this WebView. - * - * @hide + * Either {@link #grant(long) grant()} or {@link #deny()} must be called in UI + * thread to respond to the request. */ public interface PermissionRequest { /** @@ -62,8 +59,6 @@ public interface PermissionRequest { * must be equals or a subset of granted resources. * This parameter is designed to avoid granting permission by accident * especially when new resources are requested by web content. - * Calling grant(getResources()) has security issue, the new permission - * will be granted without being noticed. */ public void grant(long resources); diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 60cba86..d630a9a 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -304,7 +304,6 @@ public class WebChromeClient { * If this method isn't overridden, the permission is denied. * * @param request the PermissionRequest from current web content. - * @hide */ public void onPermissionRequest(PermissionRequest request) { request.deny(); @@ -314,8 +313,7 @@ public class WebChromeClient { * Notify the host application that the given permission request * has been canceled. Any related UI should therefore be hidden. * - * @param request the PermissionRequest need be canceled. - * @hide + * @param request the PermissionRequest that needs be canceled. */ public void onPermissionRequestCanceled(PermissionRequest request) {} diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4b2b52c..91ca7b4 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1682,13 +1682,15 @@ public class WebView extends AbsoluteLayout /** * Preauthorize the given origin to access resources. - * This authorization only valid for this WebView instance life cycle and + * The authorization only valid for this WebView instance's life cycle and * will not retained. * + * In the case that an origin has had resources preauthorized, calls to + * {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will not be + * made for those resources from that origin. + * * @param origin the origin authorized to access resources * @param resources the resource authorized to be accessed by origin. - * - * @hide */ public void preauthorizePermission(Uri origin, long resources) { checkThread(); diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java index cde8080..99a7886 100644 --- a/core/java/android/widget/ShareActionProvider.java +++ b/core/java/android/widget/ShareActionProvider.java @@ -276,6 +276,13 @@ public class ShareActionProvider extends ActionProvider { * @see Intent#ACTION_SEND_MULTIPLE */ public void setShareIntent(Intent shareIntent) { + if (shareIntent != null) { + final String action = shareIntent.getAction(); + if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) { + shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | + Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS); + } + } ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); dataModel.setIntent(shareIntent); @@ -292,7 +299,12 @@ public class ShareActionProvider extends ActionProvider { final int itemId = item.getItemId(); Intent launchIntent = dataModel.chooseActivity(itemId); if (launchIntent != null) { - launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + final String action = launchIntent.getAction(); + if (Intent.ACTION_SEND.equals(action) || + Intent.ACTION_SEND_MULTIPLE.equals(action)) { + launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | + Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS); + } mContext.startActivity(launchIntent); } return true; @@ -308,7 +320,7 @@ public class ShareActionProvider extends ActionProvider { return; } if (mOnChooseActivityListener == null) { - mOnChooseActivityListener = new ShareAcitivityChooserModelPolicy(); + mOnChooseActivityListener = new ShareActivityChooserModelPolicy(); } ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName); dataModel.setOnChooseActivityListener(mOnChooseActivityListener); @@ -317,7 +329,7 @@ public class ShareActionProvider extends ActionProvider { /** * Policy that delegates to the {@link OnShareTargetSelectedListener}, if such. */ - private class ShareAcitivityChooserModelPolicy implements OnChooseActivityListener { + private class ShareActivityChooserModelPolicy implements OnChooseActivityListener { @Override public boolean onChooseActivity(ActivityChooserModel host, Intent intent) { if (mOnShareTargetSelectedListener != null) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b91111d..8f073de 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -289,6 +289,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance(); private float mShadowRadius, mShadowDx, mShadowDy; + private int mShadowColor; + private boolean mPreDrawRegistered; @@ -2755,6 +2757,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mShadowRadius = radius; mShadowDx = dx; mShadowDy = dy; + mShadowColor = color; // Will change text clip region if (mEditor != null) mEditor.invalidateTextDisplayList(); @@ -2804,7 +2807,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_shadowColor */ public int getShadowColor() { - return mTextPaint.shadowColor; + return mShadowColor; } /** diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 1eda373..106ac0b 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -33,6 +33,14 @@ public class ChooserActivity extends ResolverActivity { return; } Intent target = (Intent)targetParcelable; + if (target != null) { + final String action = target.getAction(); + if (Intent.ACTION_SEND.equals(action) || + Intent.ACTION_SEND_MULTIPLE.equals(action)) { + target.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | + Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS); + } + } CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE); if (title == null) { title = getResources().getText(com.android.internal.R.string.chooseActivity); @@ -43,13 +51,19 @@ public class ChooserActivity extends ResolverActivity { initialIntents = new Intent[pa.length]; for (int i=0; i<pa.length; i++) { if (!(pa[i] instanceof Intent)) { - Log.w("ChooserActivity", "Initial intent #" + i - + " not an Intent: " + pa[i]); + Log.w("ChooserActivity", "Initial intent #" + i + " not an Intent: " + pa[i]); finish(); super.onCreate(null); return; } - initialIntents[i] = (Intent)pa[i]; + final Intent in = (Intent) pa[i]; + final String action = in.getAction(); + if (Intent.ACTION_SEND.equals(action) || + Intent.ACTION_SEND_MULTIPLE.equals(action)) { + in.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | + Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS); + } + initialIntents[i] = in; } } super.onCreate(savedInstanceState, target, title, initialIntents, null, false); diff --git a/core/java/com/android/internal/app/IUsageStats.aidl b/core/java/com/android/internal/app/IUsageStats.aidl index 1ea7409..7e7f0e1 100644 --- a/core/java/com/android/internal/app/IUsageStats.aidl +++ b/core/java/com/android/internal/app/IUsageStats.aidl @@ -16,13 +16,17 @@ package com.android.internal.app; +import android.app.UsageStats; import android.content.ComponentName; -import com.android.internal.os.PkgUsageStats; +import android.content.res.Configuration; +import android.os.ParcelableParcel; interface IUsageStats { void noteResumeComponent(in ComponentName componentName); void notePauseComponent(in ComponentName componentName); void noteLaunchTime(in ComponentName componentName, int millis); - PkgUsageStats getPkgUsageStats(in ComponentName componentName); - PkgUsageStats[] getAllPkgUsageStats(); + void noteStartConfig(in Configuration config); + UsageStats.PackageStats getPkgUsageStats(String callingPkg, in ComponentName componentName); + UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg); + ParcelableParcel getCurrentStats(String callingPkg); } diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index 8cdaf91..abd1791 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -18,156 +18,122 @@ package com.android.internal.app; import android.app.Activity; import android.content.ActivityNotFoundException; +import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; +import android.graphics.Color; import android.graphics.Typeface; -import android.provider.Settings; import android.os.Build; import android.os.Bundle; -import android.os.Handler; -import android.text.method.AllCapsTransformationMethod; +import android.provider.Settings; +import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.View; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.AnticipateOvershootInterpolator; -import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; -import android.widget.ImageView; import android.widget.TextView; public class PlatLogoActivity extends Activity { - FrameLayout mContent; - int mCount; - final Handler mHandler = new Handler(); - static final int BGCOLOR = 0xffed1d24; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - DisplayMetrics metrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(metrics); - - Typeface bold = Typeface.create("sans-serif", Typeface.BOLD); - Typeface light = Typeface.create("sans-serif-light", Typeface.NORMAL); - - mContent = new FrameLayout(this); - mContent.setBackgroundColor(0xC0000000); - - final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - FrameLayout.LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER; - - final ImageView logo = new ImageView(this); - logo.setImageResource(com.android.internal.R.drawable.platlogo); - logo.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - logo.setVisibility(View.INVISIBLE); - - final View bg = new View(this); - bg.setBackgroundColor(BGCOLOR); - bg.setAlpha(0f); - - final TextView letter = new TextView(this); - - letter.setTypeface(bold); - letter.setTextSize(300); - letter.setTextColor(0xFFFFFFFF); - letter.setGravity(Gravity.CENTER); - letter.setText(String.valueOf(Build.ID).substring(0, 1)); - - final int p = (int)(4 * metrics.density); - - final TextView tv = new TextView(this); - if (light != null) tv.setTypeface(light); - tv.setTextSize(30); - tv.setPadding(p, p, p, p); - tv.setTextColor(0xFFFFFFFF); - tv.setGravity(Gravity.CENTER); - tv.setTransformationMethod(new AllCapsTransformationMethod(this)); - tv.setText("Android " + Build.VERSION.RELEASE); - tv.setVisibility(View.INVISIBLE); - - mContent.addView(bg); - mContent.addView(letter, lp); - mContent.addView(logo, lp); + private static class Torso extends FrameLayout { + boolean mAnimate = false; + TextView mText; + + public Torso(Context context) { + this(context, null); + } + public Torso(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + public Torso(Context context, AttributeSet attrs, int flags) { + super(context, attrs, flags); + + for (int i=0; i<2; i++) { + final View v = new View(context); + v.setBackgroundColor(i % 2 == 0 ? Color.BLUE : Color.RED); + addView(v); + } - final FrameLayout.LayoutParams lp2 = new FrameLayout.LayoutParams(lp); - lp2.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; - lp2.bottomMargin = 10*p; + mText = new TextView(context); + mText.setTextColor(Color.BLACK); + mText.setTextSize(14 /* sp */); + mText.setTypeface(Typeface.create("monospace", Typeface.BOLD)); - mContent.addView(tv, lp2); + addView(mText, new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + Gravity.BOTTOM | Gravity.LEFT + )); + } - mContent.setOnClickListener(new View.OnClickListener() { - int clicks; + private Runnable mRunnable = new Runnable() { @Override - public void onClick(View v) { - clicks++; - if (clicks >= 6) { - mContent.performLongClick(); - return; + public void run() { + mText.setText(String.format("android_%s.flv - build %s", + Build.VERSION.CODENAME, + Build.VERSION.INCREMENTAL)); + final int N = getChildCount(); + final float parentw = getMeasuredWidth(); + final float parenth = getMeasuredHeight(); + for (int i=0; i<N; i++) { + final View v = getChildAt(i); + if (v instanceof TextView) continue; + + final int w = (int) (Math.random() * parentw); + final int h = (int) (Math.random() * parenth); + v.setLayoutParams(new FrameLayout.LayoutParams(w, h)); + + v.setX((float) Math.random() * (parentw - w)); + v.setY((float) Math.random() * (parenth - h)); } - letter.animate().cancel(); - final float offset = (int)letter.getRotation() % 360; - letter.animate() - .rotationBy((Math.random() > 0.5f ? 360 : -360) - offset) - .setInterpolator(new DecelerateInterpolator()) - .setDuration(700).start(); - } - }); - mContent.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (logo.getVisibility() != View.VISIBLE) { - bg.setScaleX(0.01f); - bg.animate().alpha(1f).scaleX(1f).setStartDelay(500).start(); - letter.animate().alpha(0f).scaleY(0.5f).scaleX(0.5f) - .rotationBy(360) - .setInterpolator(new AccelerateInterpolator()) - .setDuration(1000) - .start(); - logo.setAlpha(0f); - logo.setVisibility(View.VISIBLE); - logo.setScaleX(0.5f); - logo.setScaleY(0.5f); - logo.animate().alpha(1f).scaleX(1f).scaleY(1f) - .setDuration(1000).setStartDelay(500) - .setInterpolator(new AnticipateOvershootInterpolator()) - .start(); - tv.setAlpha(0f); - tv.setVisibility(View.VISIBLE); - tv.animate().alpha(1f).setDuration(1000).setStartDelay(1000).start(); - return true; - } - return false; + if (mAnimate) postDelayed(this, 1000); } - }); + }; + @Override + protected void onAttachedToWindow() { + mAnimate = true; + post(mRunnable); + } + @Override + protected void onDetachedFromWindow() { + mAnimate = false; + removeCallbacks(mRunnable); + } + } - logo.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (Settings.System.getLong(getContentResolver(), Settings.System.EGG_MODE, 0) - == 0) { - // For posterity: the moment this user unlocked the easter egg - Settings.System.putLong(getContentResolver(), - Settings.System.EGG_MODE, - System.currentTimeMillis()); - } - try { - startActivity(new Intent(Intent.ACTION_MAIN) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - .addCategory("com.android.internal.category.PLATLOGO")); - } catch (ActivityNotFoundException ex) { - android.util.Log.e("PlatLogoActivity", "Couldn't catch a break."); - } - finish(); - return true; - } - }); - - setContentView(mContent); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final Torso t = new Torso(this); + t.setBackgroundColor(Color.WHITE); + + t.getChildAt(0) + .setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + final ContentResolver cr = getContentResolver(); + if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) + == 0) { + // For posterity: the moment this user unlocked the easter egg + Settings.System.putLong(cr, + Settings.System.EGG_MODE, + System.currentTimeMillis()); + } + try { + startActivity(new Intent(Intent.ACTION_MAIN) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + .addCategory("com.android.internal.category.PLATLOGO")); + } catch (ActivityNotFoundException ex) { + android.util.Log.e("PlatLogoActivity", "Couldn't catch a break."); + } + finish(); + return true; + } + }); + + setContentView(t); } } diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java deleted file mode 100644 index 8c2c405..0000000 --- a/core/java/com/android/internal/os/PkgUsageStats.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.os; - -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.HashMap; -import java.util.Map; - -/** - * implementation of PkgUsageStats associated with an - * application package. - * @hide - */ -public class PkgUsageStats implements Parcelable { - public String packageName; - public int launchCount; - public long usageTime; - public Map<String, Long> componentResumeTimes; - - public static final Parcelable.Creator<PkgUsageStats> CREATOR - = new Parcelable.Creator<PkgUsageStats>() { - public PkgUsageStats createFromParcel(Parcel in) { - return new PkgUsageStats(in); - } - - public PkgUsageStats[] newArray(int size) { - return new PkgUsageStats[size]; - } - }; - - public String toString() { - return "PkgUsageStats{" - + Integer.toHexString(System.identityHashCode(this)) - + " " + packageName + "}"; - } - - public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) { - packageName = pkgName; - launchCount = count; - usageTime = time; - componentResumeTimes = new HashMap<String, Long>(lastResumeTimes); - } - - public PkgUsageStats(Parcel source) { - packageName = source.readString(); - launchCount = source.readInt(); - usageTime = source.readLong(); - final int N = source.readInt(); - componentResumeTimes = new HashMap<String, Long>(N); - for (int i = 0; i < N; i++) { - String component = source.readString(); - long lastResumeTime = source.readLong(); - componentResumeTimes.put(component, lastResumeTime); - } - } - - public PkgUsageStats(PkgUsageStats pStats) { - packageName = pStats.packageName; - launchCount = pStats.launchCount; - usageTime = pStats.usageTime; - componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes); - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int parcelableFlags) { - dest.writeString(packageName); - dest.writeInt(launchCount); - dest.writeLong(usageTime); - dest.writeInt(componentResumeTimes.size()); - for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) { - dest.writeString(ent.getKey()); - dest.writeLong(ent.getValue()); - } - } -} diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index d1d1a52..75feb5d 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -33,7 +33,8 @@ oneway interface IStatusBar void animateCollapsePanels(); void setSystemUiVisibility(int vis, int mask); void topAppWindowChanged(boolean menuVisible); - void setImeWindowStatus(in IBinder token, int vis, int backDisposition); + void setImeWindowStatus(in IBinder token, int vis, int backDisposition, + boolean showImeSwitcher); void setHardKeyboardStatus(boolean available, boolean enabled); void toggleRecentApps(); void preloadRecentApps(); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index caa6b98..cf334c3 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -31,7 +31,8 @@ interface IStatusBarService void setIconVisibility(String slot, boolean visible); void removeIcon(String slot); void topAppWindowChanged(boolean menuVisible); - void setImeWindowStatus(in IBinder token, int vis, int backDisposition); + void setImeWindowStatus(in IBinder token, int vis, int backDisposition, + boolean showImeSwitcher); void expandSettingsPanel(); void setCurrentUser(int newUserId); diff --git a/core/java/com/android/internal/util/LegacyNotificationUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java index 0394bbc..f38cbde 100644 --- a/core/java/com/android/internal/util/LegacyNotificationUtil.java +++ b/core/java/com/android/internal/util/NotificationColorUtil.java @@ -24,6 +24,7 @@ import android.graphics.Color; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.TextAppearanceSpan; @@ -38,21 +39,21 @@ import java.util.WeakHashMap; * * @hide */ -public class LegacyNotificationUtil { +public class NotificationColorUtil { - private static final String TAG = "LegacyNotificationUtil"; + private static final String TAG = "NotificationColorUtil"; private static final Object sLock = new Object(); - private static LegacyNotificationUtil sInstance; + private static NotificationColorUtil sInstance; private final ImageUtils mImageUtils = new ImageUtils(); private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache = new WeakHashMap<Bitmap, Pair<Boolean, Integer>>(); - public static LegacyNotificationUtil getInstance() { + public static NotificationColorUtil getInstance() { synchronized (sLock) { if (sInstance == null) { - sInstance = new LegacyNotificationUtil(); + sInstance = new NotificationColorUtil(); } return sInstance; } @@ -107,6 +108,9 @@ public class LegacyNotificationUtil { AnimationDrawable ad = (AnimationDrawable) d; int count = ad.getNumberOfFrames(); return count > 0 && isGrayscale(ad.getFrame(0)); + } else if (d instanceof VectorDrawable) { + // We just assume you're doing the right thing if using vectors + return true; } else { return false; } diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index b380403..bc92c4a 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -46,6 +46,8 @@ public class Protocol { public static final int BASE_WIFI_MONITOR = 0x00024000; public static final int BASE_WIFI_MANAGER = 0x00025000; public static final int BASE_WIFI_CONTROLLER = 0x00026000; + public static final int BASE_WIFI_SCANNER = 0x00027000; + public static final int BASE_WIFI_SCANNER_SERVICE = 0x00027100; public static final int BASE_DHCP = 0x00030000; public static final int BASE_DATA_CONNECTION = 0x00040000; public static final int BASE_DATA_CONNECTION_AC = 0x00041000; diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 674d084..bcfa036 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -35,7 +35,7 @@ import android.widget.FrameLayout; public class SwipeDismissLayout extends FrameLayout { private static final String TAG = "SwipeDismissLayout"; - private static final float DISMISS_MIN_PROGRESS = 0.6f; + private static final float DISMISS_MIN_DRAG_WIDTH_RATIO = .4f; public interface OnDismissedListener { void onDismissed(SwipeDismissLayout layout); @@ -244,7 +244,11 @@ public class SwipeDismissLayout extends FrameLayout { if (!mSwiping) { float deltaX = ev.getRawX() - mDownX; float deltaY = ev.getRawY() - mDownY; - mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2; + if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) { + mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2; + } else { + mSwiping = false; + } } } @@ -254,12 +258,7 @@ public class SwipeDismissLayout extends FrameLayout { mVelocityTracker.addMovement(ev); mVelocityTracker.computeCurrentVelocity(1000); - float velocityX = mVelocityTracker.getXVelocity(); - float absVelocityX = Math.abs(velocityX); - float absVelocityY = Math.abs(mVelocityTracker.getYVelocity()); - - if (deltaX > (getWidth() * DISMISS_MIN_PROGRESS) && - absVelocityX < mMinFlingVelocity && + if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) && ev.getRawX() >= mLastX) { mDismissed = true; } @@ -267,7 +266,7 @@ public class SwipeDismissLayout extends FrameLayout { // Check if the user tried to undo this. if (mDismissed && mSwiping) { // Check if the user's finger is actually back - if (deltaX < (getWidth() * DISMISS_MIN_PROGRESS)) { + if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO)) { mDismissed = false; } } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 667bf6c..51e2871 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -141,6 +141,7 @@ LOCAL_SRC_FILES:= \ android_util_FileObserver.cpp \ android/opengl/poly_clip.cpp.arm \ android/opengl/util.cpp.arm \ + android_server_FingerprintManager.cpp \ android_server_NetworkManagementSocketTagger.cpp \ android_server_Watchdog.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ @@ -240,8 +241,6 @@ LOCAL_SHARED_LIBRARIES += \ # <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private -LOCAL_LDLIBS += -lpthread -ldl - ifeq ($(WITH_MALLOC_LEAK_CHECK),true) LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK endif diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 08a88d1..22c17dd 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -33,6 +33,7 @@ #include "SkXfermode.h" #include "unicode/uloc.h" #include "unicode/ushape.h" +#include "utils/Blur.h" #include "TextLayout.h" // temporary for debugging @@ -776,19 +777,23 @@ public: env->ReleaseStringChars(text, textArray); } - static void setShadowLayer(JNIEnv* env, jobject jpaint, jfloat radius, + static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius, jfloat dx, jfloat dy, jint color) { - NPE_CHECK_RETURN_VOID(env, jpaint); - - SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); + SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); if (radius <= 0) { paint->setLooper(NULL); } else { - paint->setLooper(new SkBlurDrawLooper(radius, dx, dy, (SkColor)color))->unref(); + SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius); + paint->setLooper(new SkBlurDrawLooper((SkColor)color, sigma, dx, dy))->unref(); } } + static jboolean hasShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle) { + SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + return paint->getLooper() && paint->getLooper()->asABlurShadow(NULL); + } + static int breakText(JNIEnv* env, SkPaint& paint, const jchar text[], int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured, SkPaint::TextBufferDirection tbd) { @@ -968,7 +973,8 @@ static JNINativeMethod methods[] = { (void*) SkPaintGlue::getStringBounds }, {"nativeGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V", (void*) SkPaintGlue::getCharArrayBounds }, - {"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer} + {"native_setShadowLayer", "(JFFFI)V", (void*)SkPaintGlue::setShadowLayer}, + {"native_hasShadowLayer", "(J)Z", (void*)SkPaintGlue::hasShadowLayer} }; static jfieldID req_fieldID(jfieldID id) { diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index b78b131..621534e 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -221,7 +221,7 @@ static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz) } fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative", - "(Ljava/lang/Object;)V"); + "(Ljava/lang/ref/WeakReference;)V"); if (fields.postEvent == NULL) { ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative"); } @@ -338,7 +338,7 @@ static void SurfaceTexture_release(JNIEnv* env, jobject thiz) static JNINativeMethod gSurfaceTextureMethods[] = { {"nativeClassInit", "()V", (void*)SurfaceTexture_classInit }, - {"nativeInit", "(IZLjava/lang/Object;)V", (void*)SurfaceTexture_init }, + {"nativeInit", "(IZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init }, {"nativeFinalize", "()V", (void*)SurfaceTexture_finalize }, {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize }, {"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage }, diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp new file mode 100644 index 0000000..f8a1fd9 --- /dev/null +++ b/core/jni/android_server_FingerprintManager.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Fingerprint-JNI" + +#include "JNIHelp.h" + +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/Log.h> +#include <utils/Log.h> + +namespace android { + +static struct { + jclass clazz; + jmethodID notify; +} gFingerprintManagerClassInfo; + +static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) { + return -1; // TODO +} + +static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) { + return -1; // TODO +} + +// ---------------------------------------------------------------------------- + +static const JNINativeMethod g_methods[] = { + { "nativeEnroll", "(I)I", (void*)nativeEnroll }, + { "nativeRemove", "(I)I", (void*)nativeRemove }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find static method" methodName); + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_server_FingerprintManager(JNIEnv* env) { + FIND_CLASS(gFingerprintManagerClassInfo.clazz, + "android/service/fingerprint/FingerprintManager"); + GET_METHOD_ID(gFingerprintManagerClassInfo.notify, gFingerprintManagerClassInfo.clazz, + "notify", "(III)V"); + return AndroidRuntime::registerNativeMethods( + env, "com/android/service/fingerprint/FingerprintManager", g_methods, NELEM(g_methods)); +} + +} // namespace android diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 3aa179d..fae6698 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -79,7 +79,6 @@ using namespace uirenderer; #define RENDERER_LOGD(...) #endif -#define MODIFIER_SHADOW 1 #define MODIFIER_SHADER 2 // ---------------------------------------------------------------------------- @@ -206,32 +205,6 @@ static jint android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject cl return renderer->callDrawGLFunction(functor, dirty); } -static void android_view_GLES20Canvas_detachFunctor(JNIEnv* env, - jobject clazz, jlong rendererPtr, jlong functorPtr) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - Functor* functor = reinterpret_cast<Functor*>(functorPtr); - renderer->detachFunctor(functor); -} - -static void android_view_GLES20Canvas_attachFunctor(JNIEnv* env, - jobject clazz, jlong rendererPtr, jlong functorPtr) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - Functor* functor = reinterpret_cast<Functor*>(functorPtr); - renderer->attachFunctor(functor); -} - -static jint android_view_GLES20Canvas_invokeFunctors(JNIEnv* env, - jobject clazz, jlong rendererPtr, jobject dirty) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - android::uirenderer::Rect bounds; - status_t status = renderer->invokeFunctors(bounds); - if (status != DrawGlInfo::kStatusDone && dirty != NULL) { - env->CallVoidMethod(dirty, gRectClassInfo.set, - int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom)); - } - return status; -} - // ---------------------------------------------------------------------------- // Misc // ---------------------------------------------------------------------------- @@ -643,7 +616,6 @@ static void android_view_GLES20Canvas_drawLines(JNIEnv* env, jobject clazz, static void android_view_GLES20Canvas_resetModifiers(JNIEnv* env, jobject clazz, jlong rendererPtr, jint modifiers) { OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - if (modifiers & MODIFIER_SHADOW) renderer->resetShadow(); if (modifiers & MODIFIER_SHADER) renderer->resetShader(); } @@ -655,12 +627,6 @@ static void android_view_GLES20Canvas_setupShader(JNIEnv* env, jobject clazz, } -static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject clazz, - jlong rendererPtr, jfloat radius, jfloat dx, jfloat dy, jint color) { - OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr); - renderer->setupShadow(radius, dx, dy, color); -} - // ---------------------------------------------------------------------------- // Draw filters // ---------------------------------------------------------------------------- @@ -1007,10 +973,6 @@ static JNINativeMethod gMethods[] = { { "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize }, { "nCallDrawGLFunction", "(JJ)I", (void*) android_view_GLES20Canvas_callDrawGLFunction }, - { "nDetachFunctor", "(JJ)V", (void*) android_view_GLES20Canvas_detachFunctor }, - { "nAttachFunctor", "(JJ)V", (void*) android_view_GLES20Canvas_attachFunctor }, - { "nInvokeFunctors", "(JLandroid/graphics/Rect;)I", - (void*) android_view_GLES20Canvas_invokeFunctors }, { "nSave", "(JI)I", (void*) android_view_GLES20Canvas_save }, { "nRestore", "(J)V", (void*) android_view_GLES20Canvas_restore }, @@ -1062,7 +1024,6 @@ static JNINativeMethod gMethods[] = { { "nResetModifiers", "(JI)V", (void*) android_view_GLES20Canvas_resetModifiers }, { "nSetupShader", "(JJ)V", (void*) android_view_GLES20Canvas_setupShader }, - { "nSetupShadow", "(JFFFI)V", (void*) android_view_GLES20Canvas_setupShadow }, { "nSetupPaintFilter", "(JII)V", (void*) android_view_GLES20Canvas_setupPaintFilter }, { "nResetPaintFilter", "(J)V", (void*) android_view_GLES20Canvas_resetPaintFilter }, diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp index 2e8dccf..8a426ac 100644 --- a/core/jni/android_view_GraphicBuffer.cpp +++ b/core/jni/android_view_GraphicBuffer.cpp @@ -74,15 +74,10 @@ static struct { } gRectClassInfo; static struct { - jfieldID mFinalizer; - jfieldID mNativeCanvas; jfieldID mSurfaceFormat; + jmethodID safeCanvasSwap; } gCanvasClassInfo; -static struct { - jfieldID mNativeCanvas; -} gCanvasFinalizerClassInfo; - #define GET_INT(object, field) \ env->GetIntField(object, field) @@ -146,15 +141,6 @@ static void android_view_GraphiceBuffer_destroy(JNIEnv* env, jobject clazz, // Canvas management // ---------------------------------------------------------------------------- -static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) { - jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer); - SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>( - GET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas)); - SET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas, (long) newCanvas); - SET_LONG(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (long) newCanvas); - SkSafeUnref(previousCanvas); -} - static inline SkBitmap::Config convertPixelFormat(int32_t format) { switch (format) { case PIXEL_FORMAT_RGBA_8888: @@ -213,7 +199,7 @@ static jboolean android_view_GraphicBuffer_lockCanvas(JNIEnv* env, jobject, SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer->getPixelFormat()); SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap)); - swapCanvasPtr(env, canvas, nativeCanvas); + INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); SkRect clipRect; clipRect.set(rect.left, rect.top, rect.right, rect.bottom); @@ -233,7 +219,7 @@ static jboolean android_view_GraphicBuffer_unlockCanvasAndPost(JNIEnv* env, jobj GraphicBufferWrapper* wrapper = reinterpret_cast<GraphicBufferWrapper*>(wrapperHandle); SkCanvas* nativeCanvas = SkNEW(SkCanvas); - swapCanvasPtr(env, canvas, nativeCanvas); + INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); if (wrapper) { status_t status = wrapper->buffer->unlock(); @@ -332,13 +318,8 @@ int register_android_view_GraphicBuffer(JNIEnv* env) { GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I"); FIND_CLASS(clazz, "android/graphics/Canvas"); - GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer", - "Landroid/graphics/Canvas$CanvasFinalizer;"); - GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J"); GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I"); - - FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer"); - GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J"); + GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V"); return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index ab6c1e0..6c9d060 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -69,15 +69,10 @@ static struct { } gRectClassInfo; static struct { - jfieldID mFinalizer; - jfieldID mNativeCanvas; jfieldID mSurfaceFormat; + jmethodID safeCanvasSwap; } gCanvasClassInfo; -static struct { - jfieldID mNativeCanvas; -} gCanvasFinalizerClassInfo; - // ---------------------------------------------------------------------------- // this is just a pointer we use to pass to inc/decStrong @@ -191,15 +186,6 @@ static inline SkBitmap::Config convertPixelFormat(PixelFormat format) { } } -static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) { - jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer); - SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>( - env->GetLongField(canvasObj, gCanvasClassInfo.mNativeCanvas)); - env->SetLongField(canvasObj, gCanvasClassInfo.mNativeCanvas, (jlong)newCanvas); - env->SetLongField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (jlong)newCanvas); - SkSafeUnref(previousCanvas); -} - static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) { sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject)); @@ -247,7 +233,7 @@ static jlong nativeLockCanvas(JNIEnv* env, jclass clazz, } SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap)); - swapCanvasPtr(env, canvasObj, nativeCanvas); + env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); if (dirtyRectPtr) { nativeCanvas->clipRect( SkRect::Make(reinterpret_cast<const SkIRect&>(dirtyRect)) ); @@ -277,7 +263,7 @@ static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz, // detach the canvas from the surface SkCanvas* nativeCanvas = SkNEW(SkCanvas); - swapCanvasPtr(env, canvasObj, nativeCanvas); + env->CallVoidMethod(canvasObj, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); // unlock surface status_t err = surface->unlockAndPost(); @@ -388,12 +374,8 @@ int register_android_view_Surface(JNIEnv* env) gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "(J)V"); clazz = env->FindClass("android/graphics/Canvas"); - gCanvasClassInfo.mFinalizer = env->GetFieldID(clazz, "mFinalizer", "Landroid/graphics/Canvas$CanvasFinalizer;"); - gCanvasClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "J"); gCanvasClassInfo.mSurfaceFormat = env->GetFieldID(clazz, "mSurfaceFormat", "I"); - - clazz = env->FindClass("android/graphics/Canvas$CanvasFinalizer"); - gCanvasFinalizerClassInfo.mNativeCanvas = env->GetFieldID(clazz, "mNativeCanvas", "J"); + gCanvasClassInfo.safeCanvasSwap = env->GetMethodID(clazz, "safeCanvasSwap", "(JZ)V"); clazz = env->FindClass("android/graphics/Rect"); gRectClassInfo.left = env->GetFieldID(clazz, "left", "I"); diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp index 77ede33..9258543 100644 --- a/core/jni/android_view_TextureView.cpp +++ b/core/jni/android_view_TextureView.cpp @@ -44,16 +44,11 @@ static struct { } gRectClassInfo; static struct { - jfieldID mFinalizer; - jfieldID mNativeCanvas; jfieldID mSurfaceFormat; + jmethodID safeCanvasSwap; } gCanvasClassInfo; static struct { - jfieldID mNativeCanvas; -} gCanvasFinalizerClassInfo; - -static struct { jfieldID nativeWindow; } gTextureViewClassInfo; @@ -125,15 +120,6 @@ static void android_view_TextureView_destroyNativeWindow(JNIEnv* env, jobject te } } -static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) { - jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer); - SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>( - env->GetLongField(canvasObj, gCanvasClassInfo.mNativeCanvas)); - env->SetLongField(canvasObj, gCanvasClassInfo.mNativeCanvas, (jlong)newCanvas); - env->SetLongField(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (jlong)newCanvas); - SkSafeUnref(previousCanvas); -} - static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject, jlong nativeWindow, jobject canvas, jobject dirtyRect) { @@ -175,7 +161,7 @@ static jboolean android_view_TextureView_lockCanvas(JNIEnv* env, jobject, SET_INT(canvas, gCanvasClassInfo.mSurfaceFormat, buffer.format); SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (bitmap)); - swapCanvasPtr(env, canvas, nativeCanvas); + INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); SkRect clipRect; clipRect.set(rect.left, rect.top, rect.right, rect.bottom); @@ -193,7 +179,7 @@ static void android_view_TextureView_unlockCanvasAndPost(JNIEnv* env, jobject, jlong nativeWindow, jobject canvas) { SkCanvas* nativeCanvas = SkNEW(SkCanvas); - swapCanvasPtr(env, canvas, nativeCanvas); + INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); if (nativeWindow) { sp<ANativeWindow> window((ANativeWindow*) nativeWindow); @@ -241,13 +227,8 @@ int register_android_view_TextureView(JNIEnv* env) { GET_FIELD_ID(gRectClassInfo.bottom, clazz, "bottom", "I"); FIND_CLASS(clazz, "android/graphics/Canvas"); - GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer", - "Landroid/graphics/Canvas$CanvasFinalizer;"); - GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J"); GET_FIELD_ID(gCanvasClassInfo.mSurfaceFormat, clazz, "mSurfaceFormat", "I"); - - FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer"); - GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J"); + GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V"); FIND_CLASS(clazz, "android/view/TextureView"); GET_FIELD_ID(gTextureViewClassInfo.nativeWindow, clazz, "mNativeWindow", "J"); diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 6ff28e3..cdd036e 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -197,6 +197,12 @@ static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, proxy->setup(width, height); } +static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean opaque) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setOpaque(opaque); +} + static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop, jint dirtyRight, jint dirtyBottom) { @@ -279,6 +285,7 @@ static JNINativeMethod gMethods[] = { { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface }, { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface }, { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup }, + { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, { "nSyncAndDrawFrame", "(JJIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface }, { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 241500a..cdb77f1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1295,10 +1295,9 @@ <!-- @hide Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS} that removes restrictions on where broadcasts can be sent and allows other types of interactions. --> - <!-- TODO: Remove the system protection level.--> <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" android:permissionGroup="android.permission-group.SYSTEM_TOOLS" - android:protectionLevel="signature|system" + android:protectionLevel="signature" android:label="@string/permlab_interactAcrossUsersFull" android:description="@string/permdesc_interactAcrossUsersFull" /> diff --git a/core/res/res/anim/button_state_list_anim_quantum.xml b/core/res/res/anim/button_state_list_anim_quantum.xml new file mode 100644 index 0000000..01989a4 --- /dev/null +++ b/core/res/res/anim/button_state_list_anim_quantum.xml @@ -0,0 +1,34 @@ +<?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. +--> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:state_enabled="true"> + <set> + <objectAnimator android:propertyName="translationZ" + android:duration="@integer/button_pressed_animation_duration" + android:valueTo="@dimen/button_pressed_z" + android:valueType="floatType"/> + </set> + </item> + <!-- base state --> + <item> + <set> + <objectAnimator android:propertyName="translationZ" + android:duration="@integer/button_pressed_animation_duration" + android:valueTo="0" + android:valueType="floatType"/> + </set> + </item> +</selector>
\ No newline at end of file diff --git a/core/res/res/drawable-hdpi/stat_sys_adb_am.png b/core/res/res/drawable-hdpi/stat_sys_adb_am.png Binary files differdeleted file mode 100644 index dad614c..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_adb_am.png +++ /dev/null diff --git a/core/res/res/drawable-ldpi/stat_sys_adb_am.png b/core/res/res/drawable-ldpi/stat_sys_adb_am.png Binary files differdeleted file mode 100644 index 0171adb..0000000 --- a/core/res/res/drawable-ldpi/stat_sys_adb_am.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_adb_am.png b/core/res/res/drawable-mdpi/stat_sys_adb_am.png Binary files differdeleted file mode 100644 index 5482f34..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_adb_am.png +++ /dev/null diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png Binary files differdeleted file mode 100644 index 6351c2d..0000000 --- a/core/res/res/drawable-nodpi/platlogo.png +++ /dev/null diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml new file mode 100644 index 0000000..8eb00fa --- /dev/null +++ b/core/res/res/drawable-nodpi/platlogo.xml @@ -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. +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size android:width="400dp" android:height="400dp"/> + + <viewport android:viewportHeight="25" android:viewportWidth="25" /> + + <group> + <path + android:name="shadow" + android:pathData="m12,2.5 a11,11 0 1,0 1,0 + M6.5,7.5 + l5,0 l0,7 l7,0 l0,5 l-12,0 z" + android:fill="#40000000" + /> + <path + android:name="circle-L-ranch" + android:pathData="m12,1.5 a11,11 0 1,0 1,0 + M6.5,6.5 + l5,0 l0,7 l7,0 l0,5 l-12,0 z" + android:fill="#FFFFFF40" + /> + </group> +</vector> + + diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml new file mode 100644 index 0000000..37df348 --- /dev/null +++ b/core/res/res/drawable-nodpi/stat_sys_adb.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="25dp" android:height="25dp"/> + + <viewport android:viewportHeight="25" android:viewportWidth="25" /> + + <group> + <path + android:name="adb" + android:pathData="m3,3l8,0l0,11l11,0l0,8l-18,0z" + android:fill="#FFFFFFFF" + /> + </group> +</vector> + + diff --git a/core/res/res/drawable-xhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xhdpi/stat_sys_adb_am.png Binary files differdeleted file mode 100644 index e53f498..0000000 --- a/core/res/res/drawable-xhdpi/stat_sys_adb_am.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png Binary files differdeleted file mode 100644 index d6018dd..0000000 --- a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png +++ /dev/null diff --git a/core/res/res/drawable/btn_check_quantum_anim.xml b/core/res/res/drawable/btn_check_quantum_anim.xml index 0600522..4b329ad 100644 --- a/core/res/res/drawable/btn_check_quantum_anim.xml +++ b/core/res/res/drawable/btn_check_quantum_anim.xml @@ -14,7 +14,6 @@ --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:trigger="state_checked" android:versionCode="1" > <size @@ -27,53 +26,8 @@ <group> <path - android:name="box1" - android:pathData="M 240,80 L 240,240 L 80,240 L 80,80 L 240,80 L 240,80 z" - android:stroke="?attr/colorControlNormal" - android:strokeWidth="20" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - </group> - <group> - <path - android:name="box2" - android:pathData="M 160,200 L 160,240 L 120,240 L 120,200 L 160,200 L 160,200 z" - android:stroke="?attr/colorControlNormal" - android:strokeWidth="10" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - </group> - <group> - <path - android:name="box3" - android:pathData="M 160,216.5 L 143.5,240 L 120,223.5 L 136.5,200 L 160,216.5 L 160,216.5 z" - android:rotation="35" - android:pivotX="140" - android:pivotY="220" - android:fill="?attr/colorControlNormal" - android:stroke="?attr/colorControlNormal" - android:strokeWidth="5" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - </group> - <group> - <path - android:name="box4" - android:pathData="M 160,216.5 L 143.5,240 L 120,223.5 L 136.5,200 L 160,216.5 L 160,216.5 z" - android:fill="?attr/colorControlActivated" - android:stroke="?attr/colorControlActivated" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - </group> - <group> - <path android:name="check" android:pathData="M 232.1,80.6 L 248.5,92.1 L 145.2,239.5 L 71.5,187.8 L 83,171.5 L 140.3,211.7 z" android:fill="?attr/colorControlActivated" /> </group> - - <animation - android:durations="300,100,0,300" - android:sequence="box1,box2,box3,box4,check" /> - </vector> diff --git a/core/res/res/drawable/notification_icon_legacy_bg.xml b/core/res/res/drawable/notification_icon_legacy_bg.xml index 4ac67c3..cc5755d 100644 --- a/core/res/res/drawable/notification_icon_legacy_bg.xml +++ b/core/res/res/drawable/notification_icon_legacy_bg.xml @@ -18,5 +18,5 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid - android:color="@color/notification_icon_legacy_bg_color"/> + android:color="@color/notification_icon_bg_color"/> </shape> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 4358dd3..790567e 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -31,11 +31,11 @@ <string name="durationDayHours" msgid="2713107458736744435">"<xliff:g id="DAYS">%1$d</xliff:g> dag <xliff:g id="HOURS">%2$d</xliff:g> uur"</string> <string name="durationDayHour" msgid="7293789639090958917">"<xliff:g id="DAYS">%1$d</xliff:g> dag <xliff:g id="HOURS">%2$d</xliff:g> uur"</string> <string name="durationHours" msgid="4266858287167358988">"<xliff:g id="HOURS">%1$d</xliff:g> uur"</string> - <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> uur <xliff:g id="MINUTES">%2$d</xliff:g> minute"</string> - <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> uur <xliff:g id="MINUTES">%2$d</xliff:g> minuut"</string> + <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> u. <xliff:g id="MINUTES">%2$d</xliff:g> min."</string> + <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> u. <xliff:g id="MINUTES">%2$d</xliff:g> min."</string> <string name="durationMinutes" msgid="3134226679883579347">"<xliff:g id="MINUTES">%1$d</xliff:g> minute"</string> - <string name="durationMinuteSeconds" msgid="1424656185379003751">"<xliff:g id="MINUTES">%1$d</xliff:g> minuut <xliff:g id="SECONDS">%2$d</xliff:g> sekondes"</string> - <string name="durationMinuteSecond" msgid="3989228718067466680">"<xliff:g id="MINUTES">%1$d</xliff:g> minuut <xliff:g id="SECONDS">%2$d</xliff:g> sekond"</string> + <string name="durationMinuteSeconds" msgid="1424656185379003751">"<xliff:g id="MINUTES">%1$d</xliff:g> min. <xliff:g id="SECONDS">%2$d</xliff:g> s."</string> + <string name="durationMinuteSecond" msgid="3989228718067466680">"<xliff:g id="MINUTES">%1$d</xliff:g> min. <xliff:g id="SECONDS">%2$d</xliff:g> s."</string> <string name="durationSeconds" msgid="8050088505238241405">"<xliff:g id="SECONDS">%1$d</xliff:g> sekondes"</string> <string name="durationSecond" msgid="985669622276420331">"<xliff:g id="SECONDS">%1$d</xliff:g> sekonde"</string> <string name="untitled" msgid="4638956954852782576">"<Titelloos>"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index dbc0d43..66b4a90 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -741,7 +741,7 @@ <string name="policylab_expirePassword" msgid="885279151847254056">"تنظیم زمان انقضای رمز ورود قفل صفحه"</string> <string name="policydesc_expirePassword" msgid="1729725226314691591">"کنترل کنید چند وقت یک بار باید گذرواژه صفحه قفل عوض شود."</string> <string name="policylab_encryptedStorage" msgid="8901326199909132915">"تنظیم رمزگذاری حافظه"</string> - <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"باید اطلاعات ذخیره شده برنامه رمزگذاری شود."</string> + <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"اطلاعات ذخیره شده برنامه باید رمزگذاری شود."</string> <string name="policylab_disableCamera" msgid="6395301023152297826">"غیر فعال کردن دوربین ها"</string> <string name="policydesc_disableCamera" msgid="2306349042834754597">"جلوگیری از استفاده از همه دوربینهای دستگاه."</string> <string name="policylab_disableKeyguardFeatures" msgid="266329104542638802">"غیرفعال کردن ویژگیها در محافظ کلید"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 03dfb12..eb2540d 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -188,10 +188,8 @@ <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string> <string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string> <string name="android_system_label" msgid="6577375335728551336">"Android-järjestelmä"</string> - <!-- no translation found for user_owner_label (2804351898001038951) --> - <skip /> - <!-- no translation found for managed_profile_label (6260850669674791528) --> - <skip /> + <string name="user_owner_label" msgid="2804351898001038951">"Henkilökohtainen"</string> + <string name="managed_profile_label" msgid="6260850669674791528">"Työ"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Maksulliset palvelut"</string> <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Suorita mahdollisesti maksullisia toimintoja."</string> <string name="permgrouplab_messages" msgid="7521249148445456662">"Omat viestit"</string> diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml index ea9ffb3..751327b 100644 --- a/core/res/res/values-ms-rMY/strings.xml +++ b/core/res/res/values-ms-rMY/strings.xml @@ -188,10 +188,8 @@ <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string> <string name="safeMode" msgid="2788228061547930246">"Mod selamat"</string> <string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string> - <!-- no translation found for user_owner_label (2804351898001038951) --> - <skip /> - <!-- no translation found for managed_profile_label (6260850669674791528) --> - <skip /> + <string name="user_owner_label" msgid="2804351898001038951">"Peribadi"</string> + <string name="managed_profile_label" msgid="6260850669674791528">"Tempat Kerja"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Perkhidmatan yang anda perlu bayar"</string> <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Melakukan perkara yang boleh mengenakan bayaran kepada anda."</string> <string name="permgrouplab_messages" msgid="7521249148445456662">"Mesej anda"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index ecec099..f9b9fd2 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -31,13 +31,13 @@ <string name="durationDayHours" msgid="2713107458736744435">"<xliff:g id="DAYS">%1$d</xliff:g> dia <xliff:g id="HOURS">%2$d</xliff:g> h"</string> <string name="durationDayHour" msgid="7293789639090958917">"<xliff:g id="DAYS">%1$d</xliff:g> dia <xliff:g id="HOURS">%2$d</xliff:g> h"</string> <string name="durationHours" msgid="4266858287167358988">"<xliff:g id="HOURS">%1$d</xliff:g> horas"</string> - <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> h <xliff:g id="MINUTES">%2$d</xliff:g> min."</string> - <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> h <xliff:g id="MINUTES">%2$d</xliff:g> min."</string> - <string name="durationMinutes" msgid="3134226679883579347">"<xliff:g id="MINUTES">%1$d</xliff:g> min."</string> - <string name="durationMinuteSeconds" msgid="1424656185379003751">"<xliff:g id="MINUTES">%1$d</xliff:g> min. <xliff:g id="SECONDS">%2$d</xliff:g> seg."</string> - <string name="durationMinuteSecond" msgid="3989228718067466680">"<xliff:g id="MINUTES">%1$d</xliff:g> min. <xliff:g id="SECONDS">%2$d</xliff:g> seg."</string> - <string name="durationSeconds" msgid="8050088505238241405">"<xliff:g id="SECONDS">%1$d</xliff:g> seg."</string> - <string name="durationSecond" msgid="985669622276420331">"<xliff:g id="SECONDS">%1$d</xliff:g> seg."</string> + <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> h <xliff:g id="MINUTES">%2$d</xliff:g> min"</string> + <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> h <xliff:g id="MINUTES">%2$d</xliff:g> min"</string> + <string name="durationMinutes" msgid="3134226679883579347">"<xliff:g id="MINUTES">%1$d</xliff:g> min"</string> + <string name="durationMinuteSeconds" msgid="1424656185379003751">"<xliff:g id="MINUTES">%1$d</xliff:g> min <xliff:g id="SECONDS">%2$d</xliff:g> seg"</string> + <string name="durationMinuteSecond" msgid="3989228718067466680">"<xliff:g id="MINUTES">%1$d</xliff:g> min <xliff:g id="SECONDS">%2$d</xliff:g> seg"</string> + <string name="durationSeconds" msgid="8050088505238241405">"<xliff:g id="SECONDS">%1$d</xliff:g> seg"</string> + <string name="durationSecond" msgid="985669622276420331">"<xliff:g id="SECONDS">%1$d</xliff:g> seg"</string> <string name="untitled" msgid="4638956954852782576">"<Sem nome>"</string> <string name="ellipsis" msgid="7899829516048813237">"…"</string> <string name="ellipsis_two_dots" msgid="1228078994866030736">"‥"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 93107ca..be2ed29 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -188,10 +188,8 @@ <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string> <string name="safeMode" msgid="2788228061547930246">"Modo de segurança"</string> <string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string> - <!-- no translation found for user_owner_label (2804351898001038951) --> - <skip /> - <!-- no translation found for managed_profile_label (6260850669674791528) --> - <skip /> + <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string> + <string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Serviços que geram gastos"</string> <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Faça coisas que podem custar dinheiro."</string> <string name="permgrouplab_messages" msgid="7521249148445456662">"Suas mensagens"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 568e093..33a5a81 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -188,10 +188,8 @@ <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"˃999"</string> <string name="safeMode" msgid="2788228061547930246">"Mod sigur"</string> <string name="android_system_label" msgid="6577375335728551336">"Sistemul Android"</string> - <!-- no translation found for user_owner_label (2804351898001038951) --> - <skip /> - <!-- no translation found for managed_profile_label (6260850669674791528) --> - <skip /> + <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string> + <string name="managed_profile_label" msgid="6260850669674791528">"Serviciu"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Servicii cu plată"</string> <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Efectuează acţiuni care sunt cu plată."</string> <string name="permgrouplab_messages" msgid="7521249148445456662">"Mesajele dvs."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 8a0141a..c7c5a08 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -34,10 +34,10 @@ <string name="durationHourMinutes" msgid="9029176248692041549">"<xliff:g id="HOURS">%1$d</xliff:g> ч. <xliff:g id="MINUTES">%2$d</xliff:g> мин."</string> <string name="durationHourMinute" msgid="2741677355177402539">"<xliff:g id="HOURS">%1$d</xliff:g> ч. <xliff:g id="MINUTES">%2$d</xliff:g> мин."</string> <string name="durationMinutes" msgid="3134226679883579347">"<xliff:g id="MINUTES">%1$d</xliff:g> мин."</string> - <string name="durationMinuteSeconds" msgid="1424656185379003751">"<xliff:g id="MINUTES">%1$d</xliff:g> мин. <xliff:g id="SECONDS">%2$d</xliff:g> с."</string> - <string name="durationMinuteSecond" msgid="3989228718067466680">"<xliff:g id="MINUTES">%1$d</xliff:g> мин. <xliff:g id="SECONDS">%2$d</xliff:g> с."</string> - <string name="durationSeconds" msgid="8050088505238241405">"<xliff:g id="SECONDS">%1$d</xliff:g> с."</string> - <string name="durationSecond" msgid="985669622276420331">"<xliff:g id="SECONDS">%1$d</xliff:g> с."</string> + <string name="durationMinuteSeconds" msgid="1424656185379003751">"<xliff:g id="MINUTES">%1$d</xliff:g> мин. <xliff:g id="SECONDS">%2$d</xliff:g> с"</string> + <string name="durationMinuteSecond" msgid="3989228718067466680">"<xliff:g id="MINUTES">%1$d</xliff:g> мин. <xliff:g id="SECONDS">%2$d</xliff:g> с"</string> + <string name="durationSeconds" msgid="8050088505238241405">"<xliff:g id="SECONDS">%1$d</xliff:g> с"</string> + <string name="durationSecond" msgid="985669622276420331">"<xliff:g id="SECONDS">%1$d</xliff:g> с"</string> <string name="untitled" msgid="4638956954852782576">"<Без названия>"</string> <string name="ellipsis" msgid="7899829516048813237">"…"</string> <string name="ellipsis_two_dots" msgid="1228078994866030736">"..."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 793e5bf..3476692 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -188,10 +188,8 @@ <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string> <string name="safeMode" msgid="2788228061547930246">"安全模式"</string> <string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string> - <!-- no translation found for user_owner_label (2804351898001038951) --> - <skip /> - <!-- no translation found for managed_profile_label (6260850669674791528) --> - <skip /> + <string name="user_owner_label" msgid="2804351898001038951">"个人"</string> + <string name="managed_profile_label" msgid="6260850669674791528">"企业"</string> <string name="permgrouplab_costMoney" msgid="5429808217861460401">"需要您付费的服务"</string> <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"执行可能需要您付费的操作。"</string> <string name="permgrouplab_messages" msgid="7521249148445456662">"您的信息"</string> @@ -716,8 +714,8 @@ <string name="permdesc_accessNotifications" msgid="458457742683431387">"允许该应用检索、检查并清除通知,包括其他应用发布的通知。"</string> <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"绑定到通知侦听器服务"</string> <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允许应用绑定到通知侦听器服务的顶级接口(普通应用绝不需要此权限)。"</string> - <string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"绑定到条件提供方服务"</string> - <string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"允许应用绑定到条件提供方服务的顶级接口。普通应用绝不需要此权限。"</string> + <string name="permlab_bindConditionProviderService" msgid="1180107672332704641">"绑定到条件提供程序服务"</string> + <string name="permdesc_bindConditionProviderService" msgid="1680513931165058425">"允许应用绑定到条件提供程序服务的顶级接口。普通应用绝不需要此权限。"</string> <string name="permlab_invokeCarrierSetup" msgid="3699600833975117478">"调用运营商提供的配置应用"</string> <string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"允许应用调用运营商提供的配置应用。普通应用绝不需要此权限。"</string> <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"监听网络状况的观测信息"</string> @@ -1388,7 +1386,7 @@ <string name="wallpaper_binding_label" msgid="1240087844304687662">"壁纸"</string> <string name="chooser_wallpaper" msgid="7873476199295190279">"更改壁纸"</string> <string name="notification_listener_binding_label" msgid="2014162835481906429">"通知侦听器"</string> - <string name="condition_provider_service_binding_label" msgid="1321343352906524564">"条件提供方"</string> + <string name="condition_provider_service_binding_label" msgid="1321343352906524564">"条件提供程序"</string> <string name="vpn_title" msgid="19615213552042827">"VPN 已激活"</string> <string name="vpn_title_long" msgid="6400714798049252294">"“<xliff:g id="APP">%s</xliff:g>”已激活 VPN"</string> <string name="vpn_text" msgid="3011306607126450322">"触摸可管理网络。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 547ca27..4a67538 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1169,7 +1169,7 @@ <string name="whichHomeApplication" msgid="4616420172727326782">"選取主螢幕應用程式"</string> <string name="alwaysUse" msgid="4583018368000610438">"設為預設應用程式。"</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"前往 [系統設定] > [應用程式] > [下載] 清除預設值。"</string> - <string name="chooseActivity" msgid="7486876147751803333">"選擇一種動作"</string> + <string name="chooseActivity" msgid="7486876147751803333">"選擇分享方式"</string> <string name="chooseUsbActivity" msgid="6894748416073583509">"選取要以 USB 裝置存取的應用程式"</string> <string name="noApplications" msgid="2991814273936504689">"沒有應用程式可執行這項操作。"</string> <string name="aerr_title" msgid="1905800560317137752"></string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 0326e18..d9473ec 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2364,6 +2364,9 @@ <!-- Specifies that this view should permit nested scrolling within a compatible ancestor view. --> <attr name="nestedScrollingEnabled" format="boolean" /> + + <!-- Sets the state-based animator for the View. --> + <attr name="stateListAnimator" format="reference"/> </declare-styleable> <!-- Attributes that can be assigned to a tag for a particular View. --> @@ -4255,6 +4258,11 @@ <attr name="drawable" format="reference" /> </declare-styleable> + <!-- Attributes that can be assigned to a StateListAnimator item. --> + <declare-styleable name="StateListAnimatorItem"> + <attr name="animation"/> + </declare-styleable> + <!-- Drawable used to render a geometric shape, with a gradient or a solid color. --> <declare-styleable name="GradientDrawable"> <!-- Indicates whether the drawable should intially be visible. --> @@ -4652,18 +4660,6 @@ <!-- Drawable used to draw Vector Drawables. --> <declare-styleable name="VectorDrawable"> - <!-- What event triggers the animation --> - <attr name="trigger" format="enum"> - <enum name="state_pressed" value="1" /> - <enum name="state_focused" value="2" /> - <enum name="state_hovered" value="3" /> - <enum name="state_selected" value="4" /> - <enum name="state_checkable" value="5" /> - <enum name="state_checked" value="6" /> - <enum name="state_enabled" value="7" /> - <enum name="state_activated" value="8" /> - <enum name="state_window_focused" value="9" /> - </attr> <attr name="versionCode" /> </declare-styleable> @@ -4683,42 +4679,6 @@ <attr name="height" /> </declare-styleable> - <!-- Define the animations of drawable --> - <declare-styleable name="VectorDrawableAnimation"> - <!-- Configures this animation sequence between the named paths --> - <attr name="sequence" format="string"/> - <!-- Limits an animation to only interpolate the selected variable --> - <attr name="limitTo" format="enum"> - <enum name="unlimited" value="0"/> - <enum name="path" value="1"/> - <enum name="rotation" value="2"/> - <enum name="trimPathStart" value="3"/> - <enum name="trimPathEnd" value="4"/> - <enum name="trimPathOffset" value="5"/> - </attr> - <!-- Number of times to loop this aspect of the animation --> - <attr name="repeatCount"/> - <!-- A list of times in milliseconds to transision from on path to another. - List must contain one less than the number of named paths - e.g. given sequence="A,B,C,D" durations="100,0,100" implies 100ms for the - "A" to "B" transision instantanious switch to "C" and 100ms for "C" to "D". --> - <attr name="durations" format="string" /> - <!-- The delay before stating this aspect of the animation in milli seconds --> - <attr name="startDelay" /> - <!-- when repeating how does it repeat back and forth or a to b --> - <attr name="repeatStyle" format="enum"> - <enum name="forward" value="0"/> - <enum name="reverse" value="1"/>[] - </attr> - <!-- how does the animation progress from start to finish --> - <attr name="animate" format="enum"> - <enum name="linear" value="0"/> - <enum name="easeIn" value="1"/> - <enum name="easeOut" value="2"/> - <enum name="easeInOut" value="3"/> - </attr> - </declare-styleable> - <!-- Defines the path used in Vector Drawables. --> <declare-styleable name="VectorDrawablePath"> <!-- The Name of this path --> @@ -4763,26 +4723,6 @@ </attr> <!-- sets the Miter limit for a stroked path --> <attr name="strokeMiterLimit" format="float"/> - <!-- sets a condition to be met to draw path --> - <attr name="state_pressed" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_focused" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_selected" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_window_focused" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_enabled" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_activated" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_accelerated" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_hovered" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_checked" /> - <!-- sets a condition to be met to draw path --> - <attr name="state_checkable" /> </declare-styleable> <!-- ========================== --> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index cce4dbd..b1f256e 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -885,6 +885,47 @@ be passed a persistable Bundle in their Intent.extras. --> <attr name="persistable" format="boolean" /> + <!-- Specify whether this activity should always be launched in doc-centric mode. For + values other than <code>none</code> the activity must be defined with + {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>. + This attribute can be overridden by {@link + android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}. + + <p>If this attribute is not specified, <code>none</code> will be used. + Note that this launch behavior can be changed in some ways at runtime + through the {@link android.content.Intent} flags + {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}. --> + <attr name="documentLaunchMode"> + <!-- The default mode, which will create a new task only when + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK + Intent.FLAG_ACTIVITY_NEW_TASK} is set. --> + <enum name="none" value="0" /> + <!-- All tasks will be searched for a matching Intent. If one is found + That task will cleared and restarted with the root activity receiving a call + to {@link android.app.Activity#onNewIntent Activity.onNewIntent}. If no + such task is found a new task will be created. + This is the equivalent of with {@link + android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT} + without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK + Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. --> + <enum name="intoExisting" value="1" /> + <!-- A new task rooted at this activity will be created. + This is the equivalent of with {@link + android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT} + paired with {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK + Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. --> + <enum name="always" value="2" /> + </attr> + + <!-- Tasks launched by activities with this attribute will remain in the recent task + list until the last activity in the task is completed. When that happens the task + will be automatically removed from the recent task list. + + This attribute is the equivalent of {@link + android.content.Intent#FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS + Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} --> + <attr name="autoRemoveFromRecents" format="boolean" /> + <!-- The <code>manifest</code> tag is the root of an <code>AndroidManifest.xml</code> file, describing the contents of an Android package (.apk) file. One @@ -1549,6 +1590,8 @@ <attr name="primaryUserOnly" format="boolean" /> <attr name="persistable" /> <attr name="allowEmbedded" /> + <attr name="documentLaunchMode" /> + <attr name="autoRemoveFromRecents" /> </declare-styleable> <!-- The <code>activity-alias</code> tag declares a new diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 761170d..7d3fb44 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -123,7 +123,7 @@ <drawable name="notification_template_icon_bg">#3333B5E5</drawable> <drawable name="notification_template_icon_low_bg">#0cffffff</drawable> - <color name="notification_icon_legacy_bg_color">#ff4285F4</color> + <color name="notification_icon_bg_color">#ffa3a3a3</color> <color name="notification_action_legacy_color_filter">#ff555555</color> <!-- Keyguard colors --> diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml index cebee12..53e97fd 100644 --- a/core/res/res/values/dimens_quantum.xml +++ b/core/res/res/values/dimens_quantum.xml @@ -49,4 +49,7 @@ <dimen name="floating_window_z">16dp</dimen> <dimen name="floating_window_margin">32dp</dimen> + + <!-- the amount of elevation for pressed button state--> + <dimen name="button_pressed_z">2dp</dimen> </resources> diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml index dc90bbf..e6748c4 100644 --- a/core/res/res/values/integers.xml +++ b/core/res/res/values/integers.xml @@ -19,4 +19,5 @@ <resources> <integer name="kg_carousel_angle">75</integer> <integer name="kg_glowpad_rotation_offset">0</integer> + <integer name="button_pressed_animation_duration">100</integer> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e88a6ee..4e584c0 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2116,7 +2116,6 @@ <public type="attr" name="controlY2" /> <public type="attr" name="sharedElementName" /> <public type="attr" name="transitionGroup" /> - <public type="attr" name="trigger" /> <public type="attr" name="viewportWidth" /> <public type="attr" name="viewportHeight" /> <public type="attr" name="fillOpacity" /> @@ -2125,17 +2124,12 @@ <public type="attr" name="stroke" /> <public type="attr" name="strokeOpacity" /> <public type="attr" name="strokeWidth" /> - <public type="attr" name="durations" /> - <public type="attr" name="sequence" /> - <public type="attr" name="repeatStyle" /> <public type="attr" name="trimPathStart" /> <public type="attr" name="trimPathEnd" /> <public type="attr" name="trimPathOffset" /> <public type="attr" name="strokeLineCap" /> <public type="attr" name="strokeLineJoin" /> <public type="attr" name="clipToPath" /> - <public type="attr" name="animate" /> - <public type="attr" name="limitTo" /> <public type="attr" name="requiredForProfile"/> <public type="attr" name="pinned" /> <public type="attr" name="colorControlNormal" /> @@ -2169,6 +2163,9 @@ <public type="attr" name="excludeClass" /> <public type="attr" name="hideOnContentScroll" /> <public type="attr" name="actionOverflowMenuStyle" /> + <public type="attr" name="documentLaunchMode" /> + <public type="attr" name="autoRemoveFromRecents" /> + <public type="attr" name="stateListAnimator" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index 01c3017..88a2a9f 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -364,6 +364,7 @@ please see styles_device_defaults.xml. <item name="textColor">?attr/textColorPrimary</item> <item name="minHeight">48dip</item> <item name="minWidth">96dip</item> + <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item> </style> <!-- Small bordered ink button --> @@ -375,6 +376,7 @@ please see styles_device_defaults.xml. <!-- Borderless ink button --> <style name="Widget.Quantum.Button.Borderless"> <item name="background">@drawable/btn_borderless_quantum</item> + <item name="stateListAnimator">@null</item> </style> <!-- Small borderless ink button --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a8a4b51..2bf72e8 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1664,6 +1664,7 @@ <java-symbol type="layout" name="notification_template_quantum_big_text" /> <java-symbol type="layout" name="notification_template_quantum_inbox" /> <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_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_quantum.xml b/core/res/res/values/themes_quantum.xml index 39c8beb..768fd9a 100644 --- a/core/res/res/values/themes_quantum.xml +++ b/core/res/res/values/themes_quantum.xml @@ -123,7 +123,7 @@ please see themes_device_defaults.xml. <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.TextView.ListSeparator</item> <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item> - <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item> + <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item> <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item> @@ -467,7 +467,7 @@ please see themes_device_defaults.xml. <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.Light.TextView.ListSeparator</item> <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item> - <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item> + <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item> <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item> diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java new file mode 100644 index 0000000..0f3ea0e --- /dev/null +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java @@ -0,0 +1,50 @@ +/* + * 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.bluetooth; + +import android.os.ParcelUuid; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link BluetoothUuid}. + * <p> + * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothUuidTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothUuidTest extends TestCase { + + @SmallTest + public void testUuidParser() { + byte[] uuid16 = new byte[] { + 0x0B, 0x11 }; + assertEquals(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"), + BluetoothUuid.parseUuidFrom(uuid16)); + + byte[] uuid32 = new byte[] { + 0x0B, 0x11, 0x33, (byte) 0xFE }; + assertEquals(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"), + BluetoothUuid.parseUuidFrom(uuid32)); + + byte[] uuid128 = new byte[] { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, (byte) 0xFF }; + assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"), + BluetoothUuid.parseUuidFrom(uuid128)); + } +} diff --git a/core/tests/coretests/res/anim/reset_state_anim.xml b/core/tests/coretests/res/anim/reset_state_anim.xml new file mode 100644 index 0000000..918d0a3 --- /dev/null +++ b/core/tests/coretests/res/anim/reset_state_anim.xml @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/> + <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/> + <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/> +</set>
\ No newline at end of file diff --git a/core/tests/coretests/res/anim/test_state_anim.xml b/core/tests/coretests/res/anim/test_state_anim.xml new file mode 100644 index 0000000..9e08f68 --- /dev/null +++ b/core/tests/coretests/res/anim/test_state_anim.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true"> + <set> + <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/> + <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="20" android:valueType="floatType"/> + <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="20" android:valueType="floatType"/> + </set> + </item> + <item android:state_enabled="true" android:state_pressed="false"> + <set> + <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/> + <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/> + <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/> + </set> + </item> + <!-- base state--> + <item android:animation="@anim/reset_state_anim"/> +</selector>
\ No newline at end of file diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java new file mode 100644 index 0000000..38df78d --- /dev/null +++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java @@ -0,0 +1,102 @@ +/* +* 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.animation; + +import android.test.ActivityInstrumentationTestCase2; +import android.test.UiThreadTest; +import android.util.StateSet; +import android.view.View; +import android.view.ViewGroup; + +import com.android.frameworks.coretests.R; + +import java.util.concurrent.atomic.AtomicInteger; + + +public class StateListAnimatorTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { + + public StateListAnimatorTest() { + super(BasicAnimatorActivity.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public void testInflateFromAnimator() throws Exception { + StateListAnimator stateListAnimator = AnimatorInflater + .loadStateListAnimator(getActivity(), R.anim.test_state_anim); + assertNotNull("A state list animator should be returned", stateListAnimator); + assertEquals("State list animator should have three items", 3, + stateListAnimator.getTuples().size()); + } + + @UiThreadTest + public void testAttachDetach() throws Exception { + View view = new View(getActivity()); + final AtomicInteger setStateCount = new AtomicInteger(0); + StateListAnimator stateListAnimator = new StateListAnimator() { + @Override + public void setState(int[] state) { + setStateCount.incrementAndGet(); + super.setState(state); + } + }; + view.setStateListAnimator(stateListAnimator); + assertNotNull("State list animator should have a reference to view even if it is detached", + stateListAnimator.getTarget()); + ViewGroup viewGroup = (ViewGroup) getActivity().findViewById(android.R.id.content); + int preSetStateCount = setStateCount.get(); + viewGroup.addView(view); + assertTrue("When view is attached, state list drawable's setState should be called", + preSetStateCount < setStateCount.get()); + + StateListAnimator stateListAnimator2 = new StateListAnimator(); + view.setStateListAnimator(stateListAnimator2); + assertNull("When a new state list animator is assigned, previous one should be detached", + stateListAnimator.getTarget()); + assertNull("Any running animator should be removed on detach", + stateListAnimator.getRunningAnimator()); + assertEquals("The new state list animator should be attached to the view", + view, stateListAnimator2.getTarget()); + viewGroup.removeView(view); + assertNotNull("When view is detached from window, state list animator should still keep the" + + " reference", + stateListAnimator2.getTarget()); + } + + public void testStateListLoading() throws InterruptedException { + StateListAnimator stateListAnimator = AnimatorInflater + .loadStateListAnimator(getActivity(), R.anim.test_state_anim); + assertNotNull("A state list animator should be returned", stateListAnimator); + assertEquals("Steate list animator should have two items", 3, + stateListAnimator.getTuples().size()); + StateListAnimator.Tuple tuple1 = stateListAnimator.getTuples().get(0); + assertEquals("first tuple should have one state", 1, tuple1.getSpecs().length); + assertEquals("first spec in tuple 1 should be pressed", + com.android.internal.R.attr.state_pressed, tuple1.getSpecs()[0]); + + StateListAnimator.Tuple tuple2 = stateListAnimator.getTuples().get(1); + assertEquals("Second tuple should have two specs", 2, tuple2.getSpecs().length); + assertTrue("Tuple two should match the expected state", + StateSet.stateSetMatches(tuple2.getSpecs(), + new int[]{-com.android.internal.R.attr.state_pressed, + com.android.internal.R.attr.state_enabled})); + } +} diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd index 4057736..6143b4b 100644 --- a/docs/html/guide/topics/manifest/uses-feature-element.jd +++ b/docs/html/guide/topics/manifest/uses-feature-element.jd @@ -584,9 +584,14 @@ is sensitive to delays or lag in sound input or output.</td> </tr> <tr> <td><code>android.hardware.camera.any</code></td> - <td>The application uses at least one camera facing in any direction. Use this -in preference to <code>android.hardware.camera</code> if a back-facing camera is -not required.</td> + <td>The application uses at least one camera facing in any direction, or an +external camera device if one is connected. Use this in preference to +<code>android.hardware.camera</code> if a back-facing camera is not required. + </td> +</tr> +<tr> + <td><code>android.hardware.camera.external</code></td> + <td>The application uses an external camera device if one is connected.</td> </tr> <tr> diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java index c263a84..a40085b 100644 --- a/graphics/java/android/graphics/Camera.java +++ b/graphics/java/android/graphics/Camera.java @@ -154,7 +154,7 @@ public class Camera { getMatrix(mMatrix); canvas.concat(mMatrix); } else { - nativeApplyToCanvas(canvas.mNativeCanvas); + nativeApplyToCanvas(canvas.getNativeCanvas()); } } diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index ae3eae1..5b18623 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -39,8 +39,12 @@ import javax.microedition.khronos.opengles.GL; public class Canvas { // assigned in constructors or setBitmap, freed in finalizer + private long mNativeCanvas; + /** @hide */ - public long mNativeCanvas; + public long getNativeCanvas() { + return mNativeCanvas; + } // may be null private Bitmap mBitmap; diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java index 69089b1..6ff5f4f 100644 --- a/graphics/java/android/graphics/NinePatch.java +++ b/graphics/java/android/graphics/NinePatch.java @@ -164,12 +164,12 @@ public class NinePatch { } void drawSoftware(Canvas canvas, RectF location, Paint paint) { - nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk, + nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk, paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity); } void drawSoftware(Canvas canvas, Rect location, Paint paint) { - nativeDraw(canvas.mNativeCanvas, location, mBitmap.ni(), mNativeChunk, + nativeDraw(canvas.getNativeCanvas(), location, mBitmap.ni(), mNativeChunk, paint != null ? paint.mNativePaint : 0, canvas.mDensity, mBitmap.mDensity); } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 1e1128e..457b3ea 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -55,27 +55,6 @@ public class Paint { /** * @hide */ - public boolean hasShadow; - /** - * @hide - */ - public float shadowDx; - /** - * @hide - */ - public float shadowDy; - /** - * @hide - */ - public float shadowRadius; - /** - * @hide - */ - public int shadowColor; - - /** - * @hide - */ public int mBidiFlags = BIDI_DEFAULT_LTR; static final Style[] sStyleArray = { @@ -492,12 +471,6 @@ public class Paint { mCompatScaling = 1; mInvCompatScaling = 1; - hasShadow = false; - shadowDx = 0; - shadowDy = 0; - shadowRadius = 0; - shadowColor = 0; - mBidiFlags = BIDI_DEFAULT_LTR; setTextLocale(Locale.getDefault()); setElegantTextHeight(false); @@ -538,12 +511,6 @@ public class Paint { mCompatScaling = paint.mCompatScaling; mInvCompatScaling = paint.mInvCompatScaling; - hasShadow = paint.hasShadow; - shadowDx = paint.shadowDx; - shadowDy = paint.shadowDy; - shadowRadius = paint.shadowRadius; - shadowColor = paint.shadowColor; - mBidiFlags = paint.mBidiFlags; mLocale = paint.mLocale; } @@ -1135,22 +1102,24 @@ public class Paint { * layer is removed. */ public void setShadowLayer(float radius, float dx, float dy, int color) { - hasShadow = radius > 0.0f; - shadowRadius = radius; - shadowDx = dx; - shadowDy = dy; - shadowColor = color; - nSetShadowLayer(radius, dx, dy, color); + native_setShadowLayer(mNativePaint, radius, dx, dy, color); } - - private native void nSetShadowLayer(float radius, float dx, float dy, int color); /** * Clear the shadow layer. */ public void clearShadowLayer() { - hasShadow = false; - nSetShadowLayer(0, 0, 0, 0); + setShadowLayer(0, 0, 0, 0); + } + + /** + * Checks if the paint has a shadow layer attached + * + * @return true if the paint has a shadow layer attached and false otherwise + * @hide + */ + public boolean hasShadowLayer() { + return native_hasShadowLayer(mNativePaint); } /** @@ -2295,4 +2264,8 @@ public class Paint { private static native void nativeGetCharArrayBounds(long nativePaint, char[] text, int index, int count, int bidiFlags, Rect bounds); private static native void finalizer(long nativePaint); + + private static native void native_setShadowLayer(long native_object, + float radius, float dx, float dy, int color); + private static native boolean native_hasShadowLayer(long native_object); } diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java index 25188e0..a16c099 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -107,7 +107,7 @@ public class Picture { if (mRecordingCanvas != null) { endRecording(); } - nativeDraw(canvas.mNativeCanvas, mNativePicture); + nativeDraw(canvas.getNativeCanvas(), mNativePicture); } /** diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index d877502..0862cdd 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -18,6 +18,7 @@ package android.graphics; import java.lang.ref.WeakReference; +import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -62,9 +63,8 @@ import android.view.Surface; * #updateTexImage} should not be called directly from the callback. */ public class SurfaceTexture { - - private EventHandler mEventHandler; - private OnFrameAvailableListener mOnFrameAvailableListener; + private final Looper mCreatorLooper; + private Handler mOnFrameAvailableHandler; /** * These fields are used by native code, do not access or modify. @@ -83,7 +83,8 @@ public class SurfaceTexture { /** * Exception thrown when a SurfaceTexture couldn't be created or resized. * - * @deprecated No longer thrown. {@link Surface.OutOfResourcesException} is used instead. + * @deprecated No longer thrown. {@link android.view.Surface.OutOfResourcesException} + * is used instead. */ @SuppressWarnings("serial") @Deprecated @@ -100,10 +101,10 @@ public class SurfaceTexture { * * @param texName the OpenGL texture object name (e.g. generated via glGenTextures) * - * @throws OutOfResourcesException If the SurfaceTexture cannot be created. + * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created. */ public SurfaceTexture(int texName) { - init(texName, false); + this(texName, false); } /** @@ -121,20 +122,59 @@ public class SurfaceTexture { * @param texName the OpenGL texture object name (e.g. generated via glGenTextures) * @param singleBufferMode whether the SurfaceTexture will be in single buffered mode. * - * @throws throws OutOfResourcesException If the SurfaceTexture cannot be created. + * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created. */ public SurfaceTexture(int texName, boolean singleBufferMode) { - init(texName, singleBufferMode); + mCreatorLooper = Looper.myLooper(); + nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this)); } /** * Register a callback to be invoked when a new image frame becomes available to the - * SurfaceTexture. Note that this callback may be called on an arbitrary thread, so it is not + * SurfaceTexture. + * <p> + * The callback may be called on an arbitrary thread, so it is not * safe to call {@link #updateTexImage} without first binding the OpenGL ES context to the * thread invoking the callback. + * </p> + * + * @param listener The listener to use, or null to remove the listener. */ - public void setOnFrameAvailableListener(OnFrameAvailableListener l) { - mOnFrameAvailableListener = l; + public void setOnFrameAvailableListener(@Nullable OnFrameAvailableListener listener) { + setOnFrameAvailableListener(listener, null); + } + + /** + * Register a callback to be invoked when a new image frame becomes available to the + * SurfaceTexture. + * <p> + * If a handler is specified, the callback will be invoked on that handler's thread. + * If no handler is specified, then the callback may be called on an arbitrary thread, + * so it is not safe to call {@link #updateTexImage} without first binding the OpenGL ES + * context to the thread invoking the callback. + * </p> + * + * @param listener The listener to use, or null to remove the listener. + * @param handler The handler on which the listener should be invoked, or null + * to use an arbitrary thread. + */ + public void setOnFrameAvailableListener(@Nullable final OnFrameAvailableListener listener, + @Nullable Handler handler) { + if (listener != null) { + // Although we claim the thread is arbitrary, earlier implementation would + // prefer to send the callback on the creating looper or the main looper + // so we preserve this behavior here. + Looper looper = handler != null ? handler.getLooper() : + mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper(); + mOnFrameAvailableHandler = new Handler(looper, null, true /*async*/) { + @Override + public void handleMessage(Message msg) { + listener.onFrameAvailable(SurfaceTexture.this); + } + }; + } else { + mOnFrameAvailableHandler = null; + } } /** @@ -285,49 +325,22 @@ public class SurfaceTexture { } } - private class EventHandler extends Handler { - public EventHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (mOnFrameAvailableListener != null) { - mOnFrameAvailableListener.onFrameAvailable(SurfaceTexture.this); - } - } - } - /** * This method is invoked from native code only. */ @SuppressWarnings({"UnusedDeclaration"}) - private static void postEventFromNative(Object selfRef) { - WeakReference weakSelf = (WeakReference)selfRef; - SurfaceTexture st = (SurfaceTexture)weakSelf.get(); - if (st == null) { - return; - } - - if (st.mEventHandler != null) { - Message m = st.mEventHandler.obtainMessage(); - st.mEventHandler.sendMessage(m); - } - } - - private void init(int texName, boolean singleBufferMode) throws Surface.OutOfResourcesException { - Looper looper; - if ((looper = Looper.myLooper()) != null) { - mEventHandler = new EventHandler(looper); - } else if ((looper = Looper.getMainLooper()) != null) { - mEventHandler = new EventHandler(looper); - } else { - mEventHandler = null; + private static void postEventFromNative(WeakReference<SurfaceTexture> weakSelf) { + SurfaceTexture st = weakSelf.get(); + if (st != null) { + Handler handler = st.mOnFrameAvailableHandler; + if (handler != null) { + handler.sendEmptyMessage(0); + } } - nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this)); } - private native void nativeInit(int texName, boolean singleBufferMode, Object weakSelf) + private native void nativeInit(int texName, boolean singleBufferMode, + WeakReference<SurfaceTexture> weakSelf) throws Surface.OutOfResourcesException; private native void nativeFinalize(); private native void nativeGetTransformMatrix(float[] mtx); diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java index 61b1b85..99ab4dd 100644 --- a/graphics/java/android/graphics/drawable/ShapeDrawable.java +++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java @@ -237,7 +237,7 @@ public class ShapeDrawable extends Drawable { paint.setAlpha(modulateAlpha(prevAlpha, state.mAlpha)); // only draw shape if it may affect output - if (paint.getAlpha() != 0 || paint.getXfermode() != null || paint.hasShadow) { + if (paint.getAlpha() != 0 || paint.getXfermode() != null || paint.hasShadowLayer()) { final boolean clearColorFilter; if (mTintFilter != null && paint.getColorFilter() == null) { paint.setColorFilter(mTintFilter); diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 77712b6..ff4ab98 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -14,8 +14,6 @@ package android.graphics.drawable; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; @@ -31,9 +29,6 @@ import android.graphics.Region; import android.util.AttributeSet; import android.util.Log; import android.util.Xml; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.Interpolator; -import android.view.animation.LinearInterpolator; import com.android.internal.R; @@ -46,7 +41,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; /** * This lets you create a drawable based on an XML vector graphic It can be @@ -56,8 +50,6 @@ import java.util.HashSet; * <p/> * <dl> * <dt><code><vector></code></dt> - * <dd>The attribute <code>android:trigger</code> defines a state change that - * will drive the animation</dd> * <dd>The attribute <code>android:versionCode</code> defines the version of * VectorDrawable</dd> * <dt><code><size></code></dt> @@ -68,8 +60,7 @@ import java.util.HashSet; * The size is defined using the attributes <code>android:viewportHeight</code> * <code>android:viewportWidth</code></dd> * <dt><code><group></code></dt> - * <dd>Defines a "key frame" in the animation if there is only one group the - * drawable is static 2D image that has no animation.</dd> + * <dd>Defines the static 2D image.</dd> * <dt><code><path></code></dt> * <dd>Defines paths to be drawn. The path elements must be within a group * <dl> @@ -109,48 +100,6 @@ import java.util.HashSet; * <dd>Sets the lineJoin for a stroked path: miter,round,bevel</dd></dt> * <dt><code>android:strokeMiterLimit</code> * <dd>Sets the Miter limit for a stroked path</dd></dt> - * <dt><code>android:state_pressed</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_focused</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_selected</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_window_focused</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_enabled</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_activated</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_accelerated</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_hovered</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_checked</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * <dt><code>android:state_checkable</code> - * <dd>Sets a condition to be met to draw path</dd></dt> - * </dl> - * </dd> - * <dt><code><animation></code></dt> - * <dd>Used to customize the transition between two paths - * <dl> - * <dt><code>android:sequence</code> - * <dd>Configures this animation sequence between the named paths.</dd></dt> - * <dt><code>android:limitTo</code> - * <dd>Limits an animation to only interpolate the selected variable unlimited, - * path, rotation, trimPathStart, trimPathEnd, trimPathOffset</dd></dt> - * <dt><code>android:repeatCount</code> - * <dd>Number of times to loop this aspect of the animation</dd></dt> - * <dt><code>android:durations</code> - * <dd>The duration of each step in the animation in milliseconds Must contain - * the number of named paths - 1</dd></dt> - * <dt><code>android:startDelay</code> - * <dd></dd></dt> - * <dt><code>android:repeatStyle</code> - * <dd>when repeating how does it repeat back and forth or a to b: forward, - * inAndOut</dd></dt> - * <dt><code>android:animate</code> - * <dd>linear, accelerate, decelerate, easing</dd></dt> * </dl> * </dd> */ @@ -161,8 +110,6 @@ public class VectorDrawable extends Drawable { private static final String SHAPE_VIEWPORT = "viewport"; private static final String SHAPE_GROUP = "group"; private static final String SHAPE_PATH = "path"; - private static final String SHAPE_TRANSITION = "transition"; - private static final String SHAPE_ANIMATION = "animation"; private static final String SHAPE_VECTOR = "vector"; private static final int LINECAP_BUTT = 0; @@ -173,9 +120,6 @@ public class VectorDrawable extends Drawable { private static final int LINEJOIN_ROUND = 1; private static final int LINEJOIN_BEVEL = 2; - private static final int DEFAULT_DURATION = 1000; - private static final long DEFAULT_INFINITE_DURATION = 60 * 60 * 1000; - private final VectorDrawableState mVectorState; private int mAlpha = 0xFF; @@ -202,7 +146,7 @@ public class VectorDrawable extends Drawable { final int saveCount = canvas.save(); final Rect bounds = getBounds(); canvas.translate(bounds.left, bounds.top); - mVectorState.mVAnimatedPath.draw(canvas, bounds.width(), bounds.height()); + mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height()); canvas.restoreToCount(saveCount); } @@ -255,12 +199,12 @@ public class VectorDrawable extends Drawable { @Override public int getIntrinsicWidth() { - return (int) mVectorState.mVAnimatedPath.mBaseWidth; + return (int) mVectorState.mVPathRenderer.mBaseWidth; } @Override public int getIntrinsicHeight() { - return (int) mVectorState.mVAnimatedPath.mBaseHeight; + return (int) mVectorState.mVPathRenderer.mBaseHeight; } @Override @@ -276,8 +220,8 @@ public class VectorDrawable extends Drawable { @Override public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { - final VAnimatedPath p = inflateInternal(res, parser, attrs, theme); - setAnimatedPath(p); + final VPathRenderer p = inflateInternal(res, parser, attrs, theme); + setPathRenderer(p); } @Override @@ -290,7 +234,7 @@ public class VectorDrawable extends Drawable { super.applyTheme(t); final VectorDrawableState state = mVectorState; - final VAnimatedPath path = state.mVAnimatedPath; + final VPathRenderer path = state.mVPathRenderer; if (path != null && path.canApplyTheme()) { path.applyTheme(t); } @@ -316,9 +260,9 @@ public class VectorDrawable extends Drawable { return null; } - private VAnimatedPath inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, + private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException { - final VAnimatedPath animatedPath = new VAnimatedPath(); + final VPathRenderer pathRenderer = new VPathRenderer(); boolean noSizeTag = true; boolean noViewportTag = true; @@ -336,23 +280,18 @@ public class VectorDrawable extends Drawable { path.inflate(res, attrs, theme); currentGroup.add(path); noPathTag = false; - } else if (SHAPE_ANIMATION.equals(tagName)) { - final VAnimation anim = new VAnimation(); - anim.inflate(animatedPath.mGroupList, res, attrs, theme); - animatedPath.addAnimation(anim); } else if (SHAPE_SIZE.equals(tagName)) { - animatedPath.parseSize(res, attrs); + pathRenderer.parseSize(res, attrs); noSizeTag = false; } else if (SHAPE_VIEWPORT.equals(tagName)) { - animatedPath.parseViewport(res, attrs); + pathRenderer.parseViewport(res, attrs); noViewportTag = false; } else if (SHAPE_GROUP.equals(tagName)) { currentGroup = new VGroup(); - animatedPath.mGroupList.add(currentGroup); + pathRenderer.mGroupList.add(currentGroup); noGroupTag = false; } else if (SHAPE_VECTOR.equals(tagName)) { final TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable); - animatedPath.setTrigger(a.getInteger(R.styleable.VectorDrawable_trigger, 0)); // Parsing the version information. // Right now, we only support version "1". @@ -403,23 +342,23 @@ public class VectorDrawable extends Drawable { } // post parse cleanup - animatedPath.parseFinish(); - return animatedPath; + pathRenderer.parseFinish(); + return pathRenderer; } - private void setAnimatedPath(VAnimatedPath animatedPath) { - mVectorState.mVAnimatedPath = animatedPath; + private void setPathRenderer(VPathRenderer pathRenderer) { + mVectorState.mVPathRenderer = pathRenderer; } private static class VectorDrawableState extends ConstantState { int mChangingConfigurations; - VAnimatedPath mVAnimatedPath; + VPathRenderer mVPathRenderer; Rect mPadding; public VectorDrawableState(VectorDrawableState copy) { if (copy != null) { mChangingConfigurations = copy.mChangingConfigurations; - mVAnimatedPath = new VAnimatedPath(copy.mVAnimatedPath); + mVPathRenderer = new VPathRenderer(copy.mVPathRenderer); mPadding = new Rect(copy.mPadding); } } @@ -445,35 +384,16 @@ public class VectorDrawable extends Drawable { } } - private static class VAnimatedPath { - private static final int [] TRIGGER_MAP = { - 0, - R.attr.state_pressed, - R.attr.state_focused, - R.attr.state_hovered, - R.attr.state_selected, - R.attr.state_checkable, - R.attr.state_checked, - R.attr.state_activated, - R.attr.state_focused - }; - + private static class VPathRenderer { private final Path mPath = new Path(); private final Path mRenderPath = new Path(); private final Matrix mMatrix = new Matrix(); - private ArrayList<VAnimation> mCurrentAnimList; private VPath[] mCurrentPaths; private Paint mStrokePaint; private Paint mFillPaint; private PathMeasure mPathMeasure; - private int[] mCurrentState = new int[0]; - private float mAnimationValue; - private long mTotalDuration; - private int mTrigger; - private boolean mTriggerState; - final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>(); float mBaseWidth = 1; @@ -481,11 +401,10 @@ public class VectorDrawable extends Drawable { float mViewportWidth; float mViewportHeight; - public VAnimatedPath() { + public VPathRenderer() { } - public VAnimatedPath(VAnimatedPath copy) { - mCurrentAnimList = new ArrayList<VAnimation>(copy.mCurrentAnimList); + public VPathRenderer(VPathRenderer copy) { mGroupList.addAll(copy.mGroupList); if (copy.mCurrentPaths != null) { mCurrentPaths = new VPath[copy.mCurrentPaths.length]; @@ -493,15 +412,11 @@ public class VectorDrawable extends Drawable { mCurrentPaths[i] = new VPath(copy.mCurrentPaths[i]); } } - mAnimationValue = copy.mAnimationValue; // time goes from 0 to 1 mBaseWidth = copy.mBaseWidth; mBaseHeight = copy.mBaseHeight; mViewportWidth = copy.mViewportHeight; mViewportHeight = copy.mViewportHeight; - mTotalDuration = copy.mTotalDuration; - mTrigger = copy.mTrigger; - mCurrentState = new int[0]; } public boolean canApplyTheme() { @@ -516,14 +431,6 @@ public class VectorDrawable extends Drawable { } } - final ArrayList<VAnimation> anims = mCurrentAnimList; - for (int i = anims.size() - 1; i >= 0; i--) { - final VAnimation anim = anims.get(i); - if (anim.canApplyTheme()) { - return true; - } - } - return false; } @@ -539,70 +446,6 @@ public class VectorDrawable extends Drawable { } } - final ArrayList<VAnimation> anims = mCurrentAnimList; - for (int i = anims.size() - 1; i >= 0; i--) { - final VAnimation anim = anims.get(i); - if (anim.canApplyTheme()) { - anim.applyTheme(t); - } - } - } - - public void setTrigger(int trigger){ - mTrigger = VAnimatedPath.getStateForTrigger(trigger); - } - - public long getTotalAnimationDuration() { - mTotalDuration = 0; - int size = mCurrentAnimList.size(); - for (int i = 0; i < size; i++) { - VAnimation vAnimation = mCurrentAnimList.get(i); - long t = vAnimation.getTotalDuration(); - if (t == -1) { - mTotalDuration = -1; - return -1; - } - mTotalDuration = Math.max(mTotalDuration, t); - } - - return mTotalDuration; - } - - public float getValue() { - return mAnimationValue; - } - - /** - * @param value the point along the animations to show typically between 0.0f and 1.0f - * @return true if you need to keep repeating - */ - public boolean setAnimationFraction(float value) { - getTotalAnimationDuration(); - - long animationTime = (long) (value * mTotalDuration); - - final int len = mCurrentPaths.length; - for (int i = 0; i < len; i++) { - animationTime = - (long) ((mTotalDuration == -1) ? value * 1000 : mTotalDuration * value); - - final VPath path = mCurrentPaths[i]; - final int size = mCurrentAnimList.size(); - for (int j = 0; j < size; j++) { - final VAnimation vAnimation = mCurrentAnimList.get(j); - if (vAnimation.doesAdjustPath(path)) { - mCurrentPaths[i] = vAnimation.getPathAtTime(animationTime, path); - } - } - } - - mAnimationValue = value; - - if (mTotalDuration == -1) { - return true; - } else { - return animationTime < mTotalDuration; - } } public void draw(Canvas canvas, int w, int h) { @@ -612,7 +455,7 @@ public class VectorDrawable extends Drawable { } for (int i = 0; i < mCurrentPaths.length; i++) { - if (mCurrentPaths[i] != null && mCurrentPaths[i].isVisible(mCurrentState)) { + if (mCurrentPaths[i] != null) { drawPath(mCurrentPaths[i], canvas, w, h); } } @@ -694,34 +537,10 @@ public class VectorDrawable extends Drawable { } /** - * Ensure there is at least one animation for every path in group (linking them by names) * Build the "current" path based on the first group * TODO: improve memory use & performance or move to C++ */ public void parseFinish() { - final HashMap<String, VAnimation> newAnimations = new HashMap<String, VAnimation>(); - for (VGroup group : mGroupList) { - for (VPath vPath : group.getPaths()) { - if (!vPath.mAnimated) { - VAnimation ap = null; - - if (!newAnimations.containsKey(vPath.getID())) { - newAnimations.put(vPath.getID(), ap = new VAnimation()); - } else { - ap = newAnimations.get(vPath.getID()); - } - - ap.addPath(vPath); - vPath.mAnimated = true; - } - } - } - - if (mCurrentAnimList == null) { - mCurrentAnimList = new ArrayList<VectorDrawable.VAnimation>(); - } - mCurrentAnimList.addAll(newAnimations.values()); - final Collection<VPath> paths = mGroupList.get(0).getPaths(); mCurrentPaths = paths.toArray(new VPath[paths.size()]); for (int i = 0; i < mCurrentPaths.length; i++) { @@ -729,34 +548,6 @@ public class VectorDrawable extends Drawable { } } - public void setState(int[] state) { - mCurrentState = Arrays.copyOf(state, state.length); - } - - int getTrigger(int []state){ - if (mTrigger == 0) return 0; - for (int i = 0; i < state.length; i++) { - if (state[i] == mTrigger){ - if (mTriggerState) - return 0; - mTriggerState = true; - return 1; - } - } - if (mTriggerState) { - mTriggerState = false; - return -1; - } - return 0; - } - - public void addAnimation(VAnimation anim) { - if (mCurrentAnimList == null) { - mCurrentAnimList = new ArrayList<VectorDrawable.VAnimation>(); - } - mCurrentAnimList.add(anim); - } - private void parseViewport(Resources r, AttributeSet attrs) throws XmlPullParserException { final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableViewport); @@ -781,329 +572,6 @@ public class VectorDrawable extends Drawable { a.recycle(); } - private static final int getStateForTrigger(int trigger) { - return TRIGGER_MAP[trigger]; - } - } - - private static class VAnimation { - private static final String SEPARATOR = ","; - - private static final int DIRECTION_FORWARD = 0; - private static final int DIRECTION_IN_AND_OUT = 1; - - public enum Style { - INTERPOLATE, CROSSFADE, WIPE - } - - private final HashSet<String> mSeqMap = new HashSet<String>(); - - private Interpolator mAnimInterpolator = new AccelerateDecelerateInterpolator(); - private VPath[] mPaths = new VPath[0]; - private long[] mDuration = { DEFAULT_DURATION }; - - private int[] mThemeAttrs; - private Style mStyle; - private int mLimitProperty = 0; - private long mStartOffset; - private long mRepeat = 1; - private long mWipeDirection; - private int mMode = DIRECTION_FORWARD; - private int mInterpolatorType; - private String mId; - - public VAnimation() { - // Empty constructor. - } - - public void inflate(ArrayList<VGroup> groups, Resources r, AttributeSet attrs, Theme theme) - throws XmlPullParserException { - String value; - String[] sp; - int name; - - final TypedArray a = r.obtainAttributes(attrs, R.styleable.VectorDrawableAnimation); - final int[] themeAttrs = a.extractThemeAttrs(); - mThemeAttrs = themeAttrs; - - value = a.getString(R.styleable.VectorDrawableAnimation_sequence); - if (value != null) { - sp = value.split(SEPARATOR); - final VectorDrawable.VPath[] paths = new VectorDrawable.VPath[sp.length]; - - for (int j = 0; j < sp.length; j++) { - mSeqMap.add(sp[j].trim()); - - final VectorDrawable.VPath path = groups.get(j).get(sp[j]); - if (path == null) { - throw new XmlPullParserException(a.getPositionDescription() - + " missing path with name: " + sp[j]); - } - - path.mAnimated = true; - paths[j] = path; - } - - setPaths(paths); - } - - name = R.styleable.VectorDrawableAnimation_durations; - value = a.getString(name); - if (value != null) { - long totalDuration = 0; - sp = value.split(SEPARATOR); - - final long[] dur = new long[sp.length]; - for (int j = 0; j < dur.length; j++) { - dur[j] = Long.parseLong(sp[j]); - totalDuration += dur[j]; - } - - if (totalDuration == 0){ - throw new XmlPullParserException(a.getPositionDescription() - + " total duration must not be zero"); - } - - setDuration(dur); - } - - setLimitProperty(a.getInt(R.styleable.VectorDrawableAnimation_limitTo, 0)); - setRepeat(a.getInt(R.styleable.VectorDrawableAnimation_repeatCount, 1)); - setStartOffset(a.getInt(R.styleable.VectorDrawableAnimation_startDelay, 0)); - setMode(a.getInt(R.styleable.VectorDrawableAnimation_repeatStyle, 0)); - - fixMissingParameters(); - - a.recycle(); - } - - public boolean canApplyTheme() { - return mThemeAttrs != null; - } - - public void applyTheme(Theme t) { - // TODO: Apply theme. - } - - public boolean doesAdjustPath(VPath path) { - return mSeqMap.contains(path.getID()); - } - - public String getId() { - if (mId == null) { - mId = mPaths[0].getID(); - for (int i = 1; i < mPaths.length; i++) { - mId += mPaths[i].getID(); - } - } - return mId; - } - - public String getPathName() { - return mPaths[0].getID(); - } - - public Style getStyle() { - return mStyle; - } - - public void setStyle(Style style) { - mStyle = style; - } - - public int getLimitProperty() { - return mLimitProperty; - } - - public void setLimitProperty(int limitProperty) { - mLimitProperty = limitProperty; - } - - public long[] getDuration() { - return mDuration; - } - - public void setDuration(long[] duration) { - mDuration = duration; - } - - public long getRepeat() { - return mRepeat; - } - - public void setRepeat(long repeat) { - mRepeat = repeat; - } - - public long getStartOffset() { - return mStartOffset; - } - - public void setStartOffset(long startOffset) { - mStartOffset = startOffset; - } - - public long getWipeDirection() { - return mWipeDirection; - } - - public void setWipeDirection(long wipeDirection) { - mWipeDirection = wipeDirection; - } - - public int getMode() { - return mMode; - } - - public void setMode(int mode) { - mMode = mode; - } - - public int getInterpolator() { - return mInterpolatorType; - } - - public void setInterpolator(int interpolator) { - mInterpolatorType = interpolator; - } - - /** - * compute the total time in milliseconds - * - * @return the total time in milliseconds the animation will take - */ - public long getTotalDuration() { - long total = mStartOffset; - if (getRepeat() == -1) { - return -1; - } - for (int i = 0; i < mDuration.length; i++) { - if (mRepeat > 1) { - total += mDuration[i] * mRepeat; - } else { - total += mDuration[i]; - } - } - return total; - } - - public void setPaths(VPath[] paths) { - mPaths = paths; - } - - public void addPath(VPath path) { - mPaths = Arrays.copyOf(mPaths, mPaths.length + 1); - mPaths[mPaths.length - 1] = path; - } - - public boolean containsPath(String pathid) { - for (int i = 0; i < mPaths.length; i++) { - if (mPaths[i].getID().equals(pathid)) { - return true; - } - } - return false; - } - - public void interpolate(VPath p1, VPath p2, float time, VPath dest) { - VPath.interpolate(time, p1, p2, dest, mLimitProperty); - } - - public VPath getPathAtTime(long milliseconds, VPath dest) { - if (mPaths.length == 1) { - dest.copyFrom(mPaths[0]); - return dest; - } - long point = milliseconds - mStartOffset; - if (point < 0) { - point = 0; - } - float time = 0; - long sum = mDuration[0]; - for (int i = 1; i < mDuration.length; i++) { - sum += mDuration[i]; - } - - if (mRepeat > 1) { - time = point / (float) (sum * mRepeat); - time = mAnimInterpolator.getInterpolation(time); - - if (mMode == DIRECTION_IN_AND_OUT) { - point = ((long) (time * sum * 2 * mRepeat)) % (sum * 2); - if (point > sum) { - point = sum * 2 - point; - } - } else { - point = ((long) (time * sum * mRepeat)) % sum; - } - } else if (mRepeat == 1) { - time = point / (float) (sum * mRepeat); - time = mAnimInterpolator.getInterpolation(time); - if (mMode == DIRECTION_IN_AND_OUT) { - point = ((long) (time * sum * 2 * mRepeat)); - if (point > sum) { - point = sum * 2 - point; - } - } else { - point = Math.min(((long) (time * sum * mRepeat)), sum); - } - - } else { // repeat = -1 - if (mMode == DIRECTION_IN_AND_OUT) { - point = point % (sum * 2); - if (point > sum) { - point = sum * 2 - point; - } - time = point / (float) sum; - } else { - point = point % sum; - time = point / (float) sum; - } - } - - int transition = 0; - while (point > mDuration[transition]) { - point -= mDuration[transition++]; - } - if (mPaths.length > (transition + 1)) { - if (mPaths[transition].getID() != dest.getID()) { - dest.copyFrom(mPaths[transition]); - } - interpolate(mPaths[transition], mPaths[transition + 1], - point / (float) mDuration[transition], dest); - } else { - interpolate(mPaths[transition], mPaths[transition], 0, dest); - } - return dest; - } - - void fixMissingParameters() { - // fix missing points - float rotation = Float.NaN; - float rotationY = Float.NaN; - float rotationX = Float.NaN; - for (int i = 0; i < mPaths.length; i++) { - if (mPaths[i].mPivotX > 0) { - rotationX = mPaths[i].mPivotX; - } - if (mPaths[i].mPivotY > 0) { - rotationY = mPaths[i].mPivotY; - } - if (mPaths[i].mRotate > 0) { - rotation = mPaths[i].mRotate; - } - } - if (rotation > 0) { - for (int i = 0; i < mPaths.length; i++) { - if (mPaths[i].mPivotX == 0) { - mPaths[i].mPivotX = rotationX; - } - if (mPaths[i].mPivotY == 0) { - mPaths[i].mPivotY = rotationY; - } - } - } - } } private static class VGroup { @@ -1116,10 +584,6 @@ public class VectorDrawable extends Drawable { mVGList.add(path); } - public VPath get(String name) { - return mVGPathMap.get(name); - } - /** * Must return in order of adding * @return ordered list of paths @@ -1128,23 +592,9 @@ public class VectorDrawable extends Drawable { return mVGList; } - public int size() { - return mVGPathMap.size(); - } } private static class VPath { - private static final int LIMIT_ALL = 0; - private static final int LIMIT_PATH = 1; - private static final int LIMIT_ROTATE = 2; - private static final int LIMIT_TRIM_PATH_START = 3; - private static final int LIMIT_TRIM_PATH_OFFSET = 5; - private static final int LIMIT_TRIM_PATH_END = 4; - - private static final int STATE_UNDEFINED=0; - private static final int STATE_TRUE=1; - private static final int STATE_FALSE=2; - private static final int MAX_STATES = 10; private int[] mThemeAttrs; @@ -1165,7 +615,6 @@ public class VectorDrawable extends Drawable { float mTrimPathEnd = 1; float mTrimPathOffset = 0; - boolean mAnimated = false; boolean mClip = false; Paint.Cap mStrokeLineCap = Paint.Cap.BUTT; Paint.Join mStrokeLineJoin = Paint.Join.MITER; @@ -1176,7 +625,6 @@ public class VectorDrawable extends Drawable { private int[] mCheckState = new int[MAX_STATES]; private boolean[] mCheckValue = new boolean[MAX_STATES]; private int mNumberOfStates = 0; - private int mNumberOfTrue = 0; public VPath() { // Empty constructor. @@ -1186,38 +634,6 @@ public class VectorDrawable extends Drawable { copyFrom(p); } - public void addStateFilter(int state, boolean condition) { - int k = 0; - while (k < mNumberOfStates) { - if (mCheckState[mNumberOfStates] == state) - break; - k++; - } - mCheckState[k] = state; - mCheckValue[k] = condition; - if (k==mNumberOfStates){ - mNumberOfStates++; - } - if (condition) { - mNumberOfTrue++; - } - } - - private int getState(int state){ - for (int i = 0; i < mNumberOfStates; i++) { - if (mCheckState[mNumberOfStates] == state){ - return (mCheckValue[i])?STATE_TRUE:STATE_FALSE; - } - } - return STATE_UNDEFINED; - } - /** - * @return the name of the path - */ - public String getName() { - return mId; - } - public void toPath(Path path) { path.reset(); if (mNode != null) { @@ -1342,27 +758,6 @@ public class VectorDrawable extends Drawable { R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart); } - // TODO: Consider replacing this with existing state attributes. - final int[] states = { - R.styleable.VectorDrawablePath_state_activated, - R.styleable.VectorDrawablePath_state_checkable, - R.styleable.VectorDrawablePath_state_checked, - R.styleable.VectorDrawablePath_state_enabled, - R.styleable.VectorDrawablePath_state_focused, - R.styleable.VectorDrawablePath_state_hovered, - R.styleable.VectorDrawablePath_state_pressed, - R.styleable.VectorDrawablePath_state_selected, - R.styleable.VectorDrawablePath_state_window_focused - }; - - final int N = states.length; - for (int i = 0; i < N; i++) { - final int state = states[i]; - if (a.hasValue(state)) { - addStateFilter(state, a.getBoolean(state, false)); - } - } - updateColorAlphas(); a.recycle(); @@ -1530,7 +925,6 @@ public class VectorDrawable extends Drawable { mRotate = p1.mRotate; mPivotX = p1.mPivotX; mPivotY = p1.mPivotY; - mAnimated = p1.mAnimated; mTrimPathStart = p1.mTrimPathStart; mTrimPathEnd = p1.mTrimPathEnd; mTrimPathOffset = p1.mTrimPathOffset; @@ -1545,118 +939,6 @@ public class VectorDrawable extends Drawable { mFillRule = p1.mFillRule; } - - public static VPath interpolate(float t, VPath p1, VPath p2, VPath returnPath, int limit) { - if (limit == LIMIT_ALL || limit == LIMIT_PATH) { - if (returnPath.mNode == null || returnPath.mNode.length != p1.mNode.length) { - returnPath.mNode = new VNode[p1.mNode.length]; - } - for (int i = 0; i < returnPath.mNode.length; i++) { - if (returnPath.mNode[i] == null) { - returnPath.mNode[i] = new VNode(p1.mNode[i], p2.mNode[i], t); - } else { - returnPath.mNode[i].interpolate(p1.mNode[i], p2.mNode[i], t); - } - } - } - float t1 = 1 - t; - switch (limit) { - case LIMIT_ALL: - returnPath.mRotate = t1 * p1.mRotate + t * p2.mRotate; - returnPath.mPivotX = t1 * p1.mPivotX + t * p2.mPivotX; - returnPath.mPivotY = t1 * p1.mPivotY + t * p2.mPivotY; - returnPath.mClip = p1.mClip | p2.mClip; - - returnPath.mTrimPathStart = t1 * p1.mTrimPathStart + t * p2.mTrimPathStart; - returnPath.mTrimPathEnd = t1 * p1.mTrimPathEnd + t * p2.mTrimPathEnd; - returnPath.mTrimPathOffset = t1 * p1.mTrimPathOffset + t * p2.mTrimPathOffset; - returnPath.mStrokeMiterlimit = - t1 * p1.mStrokeMiterlimit + t * p2.mStrokeMiterlimit; - returnPath.mStrokeLineCap = p1.mStrokeLineCap; - if (returnPath.mStrokeLineCap == null) { - returnPath.mStrokeLineCap = p2.mStrokeLineCap; - } - returnPath.mStrokeLineJoin = p1.mStrokeLineJoin; - if (returnPath.mStrokeLineJoin == null) { - returnPath.mStrokeLineJoin = p2.mStrokeLineJoin; - } - returnPath.mFillRule = p1.mFillRule; - - returnPath.mStrokeColor = rgbInterpolate(t, p1.mStrokeColor, p2.mStrokeColor); - returnPath.mFillColor = rgbInterpolate(t, p1.mFillColor, p2.mFillColor); - returnPath.mStrokeWidth = t1 * p1.mStrokeWidth + t * p2.mStrokeWidth; - returnPath.mNumberOfStates = p1.mNumberOfStates; - for (int i = 0; i < returnPath.mNumberOfStates; i++) { - returnPath.mCheckState[i] = p1.mCheckState[i]; - returnPath.mCheckValue[i] = p1.mCheckValue[i]; - } - for (int i = 0; i < p2.mNumberOfStates; i++) { - returnPath.addStateFilter(p2.mCheckState[i], p2.mCheckValue[i]); - } - - int count = 0; - for (int i = 0; i < returnPath.mNumberOfStates; i++) { - if (returnPath.mCheckValue[i]) { - count++; - } - } - returnPath.mNumberOfTrue = count; - break; - case LIMIT_ROTATE: - returnPath.mRotate = t1 * p1.mRotate + t * p2.mRotate; - break; - case LIMIT_TRIM_PATH_END: - returnPath.mTrimPathEnd = t1 * p1.mTrimPathEnd + t * p2.mTrimPathEnd; - break; - case LIMIT_TRIM_PATH_OFFSET: - returnPath.mTrimPathOffset = t1 * p1.mTrimPathOffset + t * p2.mTrimPathOffset; - break; - case LIMIT_TRIM_PATH_START: - returnPath.mTrimPathStart = t1 * p1.mTrimPathStart + t * p2.mTrimPathStart; - break; - } - return returnPath; - } - - private static int rgbInterpolate(float fraction, int startColor, int endColor) { - if (startColor == endColor) { - return startColor; - } else if (startColor == 0) { - return endColor; - } else if (endColor == 0) { - return startColor; - } - - final int startA = (startColor >> 24) & 0xff; - final int startR = (startColor >> 16) & 0xff; - final int startG = (startColor >> 8) & 0xff; - final int startB = startColor & 0xff; - - final int endA = (endColor >> 24) & 0xff; - final int endR = (endColor >> 16) & 0xff; - final int endG = (endColor >> 8) & 0xff; - final int endB = endColor & 0xff; - - return ((startA + (int)(fraction * (endA - startA))) << 24) | - ((startR + (int)(fraction * (endR - startR))) << 16) | - ((startG + (int)(fraction * (endG - startG))) << 8) | - ((startB + (int)(fraction * (endB - startB)))); - } - - public boolean isVisible(int[] state) { - int match = 0; - for (int i = 0; i < state.length; i++) { - int v = getState(state[i]); - if (v != STATE_UNDEFINED) { - if (v==STATE_TRUE) { - match++; - } else { - return false; - } - } - } - return match == mNumberOfTrue; - } } private static class VNode { @@ -1673,25 +955,6 @@ public class VectorDrawable extends Drawable { mParams = Arrays.copyOf(n.mParams, n.mParams.length); } - public VNode(VNode n1, VNode n2, float t) { - mType = n1.mType; - mParams = new float[n1.mParams.length]; - interpolate(n1, n2, t); - } - - private boolean match(VNode n) { - if (n.mType != mType) { - return false; - } - return (mParams.length == n.mParams.length); - } - - public void interpolate(VNode n1, VNode n2, float t) { - for (int i = 0; i < n1.mParams.length; i++) { - mParams[i] = n1.mParams[i] * (1 - t) + n2.mParams[i] * t; - } - } - public static void createPath(VNode[] node, Path path) { float[] current = new float[4]; char previousCommand = 'm'; @@ -1747,7 +1010,6 @@ public class VectorDrawable extends Drawable { break; } for (int k = 0; k < val.length; k += incr) { - // TODO: build test to prove all permutations work switch (cmd) { case 'm': // moveto - Start a new sub-path (relative) path.rMoveTo(val[k + 0], val[k + 1]); diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index eb0cac8..2cadf09 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -6,6 +6,7 @@ include $(CLEAR_VARS) ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SRC_FILES := \ utils/Blur.cpp \ + utils/GLUtils.cpp \ utils/SortedListImpl.cpp \ thread/TaskManager.cpp \ font/CacheTexture.cpp \ @@ -53,7 +54,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) TextureCache.cpp \ TextDropShadowCache.cpp - # RenderThread stuff +# RenderThread stuff LOCAL_SRC_FILES += \ renderthread/CanvasContext.cpp \ renderthread/DrawFrameTask.cpp \ diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 3d58964..45b6624 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -190,7 +190,7 @@ public: // Overlapping other operations is only allowed for text without shadow. For other ops, // multiDraw isn't guaranteed to overdraw correctly - if (!isTextBatch || state->mDrawModifiers.mHasShadow) { + if (!isTextBatch || op->hasTextShadow()) { if (intersects(state->mBounds)) return false; } const DeferredDisplayState* lhs = state; diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index ce92beb..f1d70eb 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -183,6 +183,10 @@ public: return OpenGLRenderer::getAlphaDirect(mPaint); } + virtual bool hasTextShadow() const { + return false; + } + inline float strokeWidthOutset() { // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced // 1.0 stroke, treat 1.0 as minimum. @@ -244,11 +248,11 @@ public: bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { localBounds.set(mLocalBounds); - if (drawModifiers.mHasShadow) { - // TODO: inspect paint's looper directly + OpenGLRenderer::TextShadow textShadow; + if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) { Rect shadow(mLocalBounds); - shadow.translate(drawModifiers.mShadowDx, drawModifiers.mShadowDy); - shadow.outset(drawModifiers.mShadowRadius); + shadow.translate(textShadow.dx, textShadow.dx); + shadow.outset(textShadow.radius); localBounds.unionWith(shadow); } return true; @@ -619,41 +623,6 @@ private: SkiaShader* mShader; }; -class ResetShadowOp : public StateOp { -public: - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.resetShadow(); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOGS("ResetShadow"); - } - - virtual const char* name() { return "ResetShadow"; } -}; - -class SetupShadowOp : public StateOp { -public: - SetupShadowOp(float radius, float dx, float dy, int color) - : mRadius(radius), mDx(dx), mDy(dy), mColor(color) {} - - virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { - renderer.setupShadow(mRadius, mDx, mDy, mColor); - } - - virtual void output(int level, uint32_t logFlags) const { - OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor); - } - - virtual const char* name() { return "SetupShadow"; } - -private: - float mRadius; - float mDx; - float mDy; - int mColor; -}; - class ResetPaintFilterOp : public StateOp { public: virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -1351,6 +1320,10 @@ public: OP_LOG("Draw some text, %d bytes", mBytesCount); } + virtual bool hasTextShadow() const { + return OpenGLRenderer::hasTextShadow(mPaint); + } + virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { const SkPaint* paint = getPaint(renderer); diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 8afd106..c2ce6ed 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -410,16 +410,6 @@ void DisplayListRenderer::setupShader(SkiaShader* shader) { addStateOp(new (alloc()) SetupShaderOp(shader)); } -void DisplayListRenderer::resetShadow() { - addStateOp(new (alloc()) ResetShadowOp()); - OpenGLRenderer::resetShadow(); -} - -void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) { - addStateOp(new (alloc()) SetupShadowOp(radius, dx, dy, color)); - OpenGLRenderer::setupShadow(radius, dx, dy, color); -} - void DisplayListRenderer::resetPaintFilter() { addStateOp(new (alloc()) ResetPaintFilterOp()); } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 25e78c1..185179a 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -102,9 +102,6 @@ public: virtual void resetShader(); virtual void setupShader(SkiaShader* shader); - virtual void resetShadow(); - virtual void setupShadow(float radius, float dx, float dy, int color); - virtual void resetPaintFilter(); virtual void setupPaintFilter(int clearBits, int setBits); diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index f06106b..2268386 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -482,8 +482,8 @@ void Matrix4::decomposeScale(float& sx, float& sy) const { sy = copysignf(sqrtf(len), data[mat4::kScaleY]); } -void Matrix4::dump() const { - ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType()); +void Matrix4::dump(const char* label) const { + ALOGD("%s[simple=%d, type=0x%x", label ? label : "Matrix4", isSimple(), getType()); ALOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]); ALOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]); ALOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index 26cb05f..e33a001 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -209,7 +209,7 @@ public: void decomposeScale(float& sx, float& sy) const; - void dump() const; + void dump(const char* label = NULL) const; static const Matrix4& identity(); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 95fdb04..5a977c8 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -37,6 +37,7 @@ #include "PathTessellator.h" #include "Properties.h" #include "ShadowTessellator.h" +#include "utils/GLUtils.h" #include "Vector.h" #include "VertexBuffer.h" @@ -296,24 +297,7 @@ void OpenGLRenderer::finish() { if (!suppressErrorChecks()) { #if DEBUG_OPENGL - GLenum status = GL_NO_ERROR; - while ((status = glGetError()) != GL_NO_ERROR) { - ALOGD("GL error from OpenGLRenderer: 0x%x", status); - switch (status) { - case GL_INVALID_ENUM: - ALOGE(" GL_INVALID_ENUM"); - break; - case GL_INVALID_VALUE: - ALOGE(" GL_INVALID_VALUE"); - break; - case GL_INVALID_OPERATION: - ALOGE(" GL_INVALID_OPERATION"); - break; - case GL_OUT_OF_MEMORY: - ALOGE(" Out of memory!"); - break; - } - } + GLUtils::dumpGLErrors(); #endif #if DEBUG_MEMORY_USAGE @@ -379,49 +363,9 @@ void OpenGLRenderer::resumeAfterLayer() { dirtyClip(); } -void OpenGLRenderer::detachFunctor(Functor* functor) { - mFunctors.remove(functor); -} - -void OpenGLRenderer::attachFunctor(Functor* functor) { - mFunctors.add(functor); -} - -status_t OpenGLRenderer::invokeFunctors(Rect& dirty) { - status_t result = DrawGlInfo::kStatusDone; - size_t count = mFunctors.size(); - - if (count > 0) { - interrupt(); - SortedVector<Functor*> functors(mFunctors); - mFunctors.clear(); - - DrawGlInfo info; - info.clipLeft = 0; - info.clipTop = 0; - info.clipRight = 0; - info.clipBottom = 0; - info.isLayer = false; - info.width = 0; - info.height = 0; - memset(info.transform, 0, sizeof(float) * 16); - - for (size_t i = 0; i < count; i++) { - Functor* f = functors.itemAt(i); - result |= (*f)(DrawGlInfo::kModeProcess, &info); - } - resume(); - } - - return result; -} - status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; - detachFunctor(functor); - - Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); @@ -2694,28 +2638,32 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, FontRenderer& fontRenderer, int alpha, float x, float y) { mCaches.activeTexture(0); + TextShadow textShadow; + if (!getTextShadow(paint, &textShadow)) { + LOG_ALWAYS_FATAL("failed to query shadow attributes"); + } + // NOTE: The drop shadow will not perform gamma correction // if shader-based correction is enabled mCaches.dropShadowCache.setFontRenderer(fontRenderer); const ShadowTexture* shadow = mCaches.dropShadowCache.get( - paint, text, bytesCount, count, mDrawModifiers.mShadowRadius, positions); + paint, text, bytesCount, count, textShadow.radius, positions); // If the drop shadow exceeds the max texture size or couldn't be // allocated, skip drawing if (!shadow) return; const AutoTexture autoCleanup(shadow); - const float sx = x - shadow->left + mDrawModifiers.mShadowDx; - const float sy = y - shadow->top + mDrawModifiers.mShadowDy; + const float sx = x - shadow->left + textShadow.dx; + const float sy = y - shadow->top + textShadow.dy; - const int shadowAlpha = ((mDrawModifiers.mShadowColor >> 24) & 0xFF) * mSnapshot->alpha; - int shadowColor = mDrawModifiers.mShadowColor; + const int shadowAlpha = ((textShadow.color >> 24) & 0xFF) * mSnapshot->alpha; if (mDrawModifiers.mShader) { - shadowColor = 0xffffffff; + textShadow.color = SK_ColorWHITE; } setupDraw(); setupDrawWithTexture(true); - setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha); + setupDrawAlpha8Color(textShadow.color, shadowAlpha < 255 ? shadowAlpha : alpha); setupDrawColorFilter(getColorFilter(paint)); setupDrawShader(); setupDrawBlending(paint, true); @@ -2732,7 +2680,7 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, } bool OpenGLRenderer::canSkipText(const SkPaint* paint) const { - float alpha = (mDrawModifiers.mHasShadow ? 1.0f : paint->getAlpha()) * mSnapshot->alpha; + float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * mSnapshot->alpha; return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode; } @@ -2764,7 +2712,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) { + if (CC_UNLIKELY(hasTextShadow(paint))) { drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, 0.0f, 0.0f); } @@ -2841,7 +2789,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint); - if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) { + if (CC_UNLIKELY(hasTextShadow(paint))) { fontRenderer.setFont(paint, mat4::identity()); drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, oldX, oldY); @@ -3062,22 +3010,6 @@ void OpenGLRenderer::setupShader(SkiaShader* shader) { } /////////////////////////////////////////////////////////////////////////////// -// Drop shadow -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::resetShadow() { - mDrawModifiers.mHasShadow = false; -} - -void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) { - mDrawModifiers.mHasShadow = true; - mDrawModifiers.mShadowRadius = radius; - mDrawModifiers.mShadowDx = dx; - mDrawModifiers.mShadowDy = dy; - mDrawModifiers.mShadowColor = color; -} - -/////////////////////////////////////////////////////////////////////////////// // Draw filters /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 7794abc..1d46945 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -29,6 +29,7 @@ #include <SkShader.h> #include <SkXfermode.h> +#include <utils/Blur.h> #include <utils/Functor.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> @@ -72,13 +73,6 @@ struct DrawModifiers { SkiaShader* mShader; float mOverrideLayerAlpha; - // Drop shadow - bool mHasShadow; - float mShadowRadius; - float mShadowDx; - float mShadowDy; - int mShadowColor; - // Draw filters bool mHasDrawFilter; int mPaintFilterClearBits; @@ -151,9 +145,6 @@ public: return mCountOverdraw ? mOverdraw : 0.0f; } - ANDROID_API status_t invokeFunctors(Rect& dirty); - ANDROID_API void detachFunctor(Functor* functor); - ANDROID_API void attachFunctor(Functor* functor); virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); ANDROID_API void pushLayerUpdate(Layer* layer); @@ -229,9 +220,6 @@ public: virtual void resetShader(); virtual void setupShader(SkiaShader* shader); - virtual void resetShadow(); - virtual void setupShadow(float radius, float dx, float dy, int color); - virtual void resetPaintFilter(); virtual void setupPaintFilter(int clearBits, int setBits); @@ -319,6 +307,31 @@ public: return paint->getAlpha(); } + struct TextShadow { + SkScalar radius; + float dx; + float dy; + SkColor color; + }; + + static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) { + SkDrawLooper::BlurShadowRec blur; + if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) { + if (textShadow) { + textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma); + textShadow->dx = blur.fOffset.fX; + textShadow->dy = blur.fOffset.fY; + textShadow->color = blur.fColor; + } + return true; + } + return false; + } + + static inline bool hasTextShadow(const SkPaint* paint) { + return getTextShadow(paint, NULL); + } + /** * Return the best transform to use to rasterize text given a full * transform matrix. @@ -959,8 +972,6 @@ private: // List of rectangles to clear after saveLayer() is invoked Vector<Rect*> mLayers; - // List of functors to invoke after a frame is drawn - SortedVector<Functor*> mFunctors; // List of layers to update at the beginning of a frame Vector<Layer*> mLayerUpdates; diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 92964a8..f38d8b7 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -175,6 +175,10 @@ public: bottom += dy; } + void inset(float delta) { + outset(-delta); + } + void outset(float delta) { left -= delta; top -= delta; @@ -230,8 +234,8 @@ public: bottom = ceilf(bottom); } - void dump() const { - ALOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom); + void dump(const char* label) const { + ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom); } private: diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h index 3209a53..57db816 100644 --- a/libs/hwui/Renderer.h +++ b/libs/hwui/Renderer.h @@ -178,9 +178,6 @@ public: virtual void resetShader() = 0; virtual void setupShader(SkiaShader* shader) = 0; - virtual void resetShadow() = 0; - virtual void setupShadow(float radius, float dx, float dy, int color) = 0; - virtual void resetPaintFilter() = 0; virtual void setupPaintFilter(int clearBits, int setBits) = 0; diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index d26ee38..6bfa203 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -27,9 +27,15 @@ namespace uirenderer { // Constructors /////////////////////////////////////////////////////////////////////////////// -Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), - invisible(false), empty(false), alpha(1.0f) { - +Snapshot::Snapshot() + : flags(0) + , previous(NULL) + , layer(NULL) + , fbo(0) + , invisible(false) + , empty(false) + , height(0) + , alpha(1.0f) { transform = &mTransformRoot; clipRect = &mClipRectRoot; region = NULL; @@ -40,10 +46,16 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0), * Copies the specified snapshot/ The specified snapshot is stored as * the previous snapshot. */ -Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): - flags(0), previous(s), layer(s->layer), fbo(s->fbo), - invisible(s->invisible), empty(false), - viewport(s->viewport), height(s->height), alpha(s->alpha) { +Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) + : flags(0) + , previous(s) + , layer(s->layer) + , fbo(s->fbo) + , invisible(s->invisible) + , empty(false) + , viewport(s->viewport) + , height(s->height) + , alpha(s->alpha) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index fc3548c..5a23158 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -349,6 +349,8 @@ void CanvasContext::setSurface(EGLNativeWindowType window) { mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); mHaveNewSurface = true; makeCurrent(); + } else { + mRenderThread.removeFrameCallback(this); } } @@ -385,6 +387,10 @@ void CanvasContext::setup(int width, int height) { mCanvas->setViewport(width, height); } +void CanvasContext::setOpaque(bool opaque) { + mOpaque = opaque; +} + void CanvasContext::makeCurrent() { // TODO: Figure out why this workaround is needed, see b/13913604 // In the meantime this matches the behavior of GLRenderer, so it is not a regression @@ -468,6 +474,10 @@ void CanvasContext::draw(Rect* dirty) { // Called by choreographer to do an RT-driven animation void CanvasContext::doFrame() { + if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) { + return; + } + ATRACE_CALL(); TreeInfo info; @@ -486,10 +496,7 @@ void CanvasContext::invokeFunctor(Functor* functor) { requireGlContext(); mode = DrawGlInfo::kModeProcess; } - // TODO: Remove the dummy info in the future - DrawGlInfo dummyInfo; - memset(&dummyInfo, 0, sizeof(DrawGlInfo)); - (*functor)(mode, &dummyInfo); + (*functor)(mode, NULL); if (mCanvas) { mCanvas->resume(); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index a95e27a..dcb9858 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -52,6 +52,7 @@ public: void updateSurface(EGLNativeWindowType window); void pauseSurface(EGLNativeWindowType window); void setup(int width, int height); + void setOpaque(bool opaque); void makeCurrent(); void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info); void draw(Rect* dirty); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index c2806fa..82a2dbc 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -159,6 +159,18 @@ void RenderProxy::setup(int width, int height) { post(task); } +CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) { + args->context->setOpaque(args->opaque); + return NULL; +} + +void RenderProxy::setOpaque(bool opaque) { + SETUP_TASK(setOpaque); + args->context = mContext; + args->opaque = opaque; + post(task); +} + int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 013c3bd..4a7e70a 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -67,6 +67,7 @@ public: ANDROID_API void updateSurface(const sp<ANativeWindow>& window); ANDROID_API void pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height); + ANDROID_API void setOpaque(bool opaque); ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); ANDROID_API void destroyCanvasAndSurface(); diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp index 85d90d0..c020b40 100644 --- a/libs/hwui/utils/Blur.cpp +++ b/libs/hwui/utils/Blur.cpp @@ -23,6 +23,31 @@ namespace android { namespace uirenderer { +// This constant approximates the scaling done in the software path's +// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)). +static const float BLUR_SIGMA_SCALE = 0.57735f; + +float Blur::convertRadiusToSigma(float radius) { + return radius > 0 ? BLUR_SIGMA_SCALE * radius + 0.5f : 0.0f; +} + +float Blur::convertSigmaToRadius(float sigma) { + return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f; +} + +/** + * HWUI has used a slightly different equation than Skia to generate the value + * for sigma and to preserve compatibility we have kept that logic. + * + * Based on some experimental radius and sigma values we approximate the + * equation sigma = f(radius) as sigma = radius * 0.3 + 0.6. The larger the + * radius gets, the more our gaussian blur will resemble a box blur since with + * large sigma the gaussian curve begins to lose its shape. + */ +static float legacyConvertRadiusToSigma(float radius) { + return radius > 0 ? 0.3f * radius + 0.6f : 0.0f; +} + void Blur::generateGaussianWeights(float* weights, int32_t radius) { // Compute gaussian weights for the blur // e is the euler's number @@ -31,13 +56,7 @@ void Blur::generateGaussianWeights(float* weights, int32_t radius) { // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) // x is of the form [-radius .. 0 .. radius] // and sigma varies with radius. - // Based on some experimental radius values and sigma's - // we approximately fit sigma = f(radius) as - // sigma = radius * 0.3 + 0.6 - // The larger the radius gets, the more our gaussian blur - // will resemble a box blur since with large sigma - // the gaussian curve begins to lose its shape - float sigma = 0.3f * (float) radius + 0.6f; + float sigma = legacyConvertRadiusToSigma((float) radius); // Now compute the coefficints // We will store some redundant values to save some math during diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h index 6c176e9..79aff65 100644 --- a/libs/hwui/utils/Blur.h +++ b/libs/hwui/utils/Blur.h @@ -18,12 +18,18 @@ #define ANDROID_HWUI_BLUR_H #include <stdint.h> +#include <cutils/compiler.h> namespace android { namespace uirenderer { class Blur { public: + // If radius > 0, return the corresponding sigma, else return 0 + ANDROID_API static float convertRadiusToSigma(float radius); + // If sigma > 0.6, return the corresponding radius, else return 0 + ANDROID_API static float convertSigmaToRadius(float sigma); + static void generateGaussianWeights(float* weights, int32_t radius); static void horizontal(float* weights, int32_t radius, const uint8_t* source, uint8_t* dest, int32_t width, int32_t height); diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp new file mode 100644 index 0000000..9b298ca --- /dev/null +++ b/libs/hwui/utils/GLUtils.cpp @@ -0,0 +1,52 @@ +/* + * 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 "OpenGLRenderer" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/Log.h> + +#include "GLUtils.h" + +namespace android { +namespace uirenderer { + +void GLUtils::dumpGLErrors() { + GLenum status = GL_NO_ERROR; + while ((status = glGetError()) != GL_NO_ERROR) { + switch (status) { + case GL_INVALID_ENUM: + ALOGE("GL error: GL_INVALID_ENUM"); + break; + case GL_INVALID_VALUE: + ALOGE("GL error: GL_INVALID_VALUE"); + break; + case GL_INVALID_OPERATION: + ALOGE("GL error: GL_INVALID_OPERATION"); + break; + case GL_OUT_OF_MEMORY: + ALOGE("GL error: Out of memory!"); + break; + default: + ALOGE("GL error: 0x%x", status); + } + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h new file mode 100644 index 0000000..890e374 --- /dev/null +++ b/libs/hwui/utils/GLUtils.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef GLUTILS_H +#define GLUTILS_H + +namespace android { +namespace uirenderer { + +class GLUtils { +private: +public: + /** + * Print out any GL errors with ALOGE + */ + static void dumpGLErrors(); + +}; // class GLUtils + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* GLUTILS_H */ diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 7deabe9..8ba44dc 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -38,4 +38,4 @@ public: } /* namespace uirenderer */ } /* namespace android */ -#endif /* RENDERNODE_H */ +#endif /* MATHUTILS_H */ diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 1899685..007eb40 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -172,16 +172,14 @@ public class AudioTrack public @interface WriteMode {} /** - * @hide CANDIDATE FOR PUBLIC API * The write mode indicating the write operation will block until all data has been written, - * to be used in {@link #write(ByteBuffer, int, int, int)}. + * to be used in {@link #write(ByteBuffer, int, int)} */ public final static int WRITE_BLOCKING = 0; /** - * @hide CANDIDATE FOR PUBLIC API * The write mode indicating the write operation will return immediately after * queuing as much audio data for playback as possible without blocking, to be used in - * {@link #write(ByteBuffer, int, int, int)}. + * {@link #write(ByteBuffer, int, int)}. */ public final static int WRITE_NON_BLOCKING = 1; @@ -1220,7 +1218,6 @@ public class AudioTrack /** - * @hide CANDIDATE FOR PUBLIC API * Writes the audio data to the audio sink for playback (streaming mode), * or copies audio data for later playback (static buffer mode). * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index ca77f04..3ff07d9 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -31,8 +31,8 @@ import android.os.ResultReceiver; interface ISession { void sendEvent(String event, in Bundle data); ISessionController getController(); - void setTransportPerformerEnabled(); - void publish(); + void setFlags(int flags); + void setActive(boolean active); void destroy(); // These commands are for setting up and communicating with routes diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 84b9a0f..7a8c22e 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -15,6 +15,7 @@ package android.media.session; +import android.content.ComponentName; import android.media.session.ISession; import android.media.session.ISessionCallback; import android.os.Bundle; @@ -25,4 +26,5 @@ import android.os.Bundle; */ interface ISessionManager { ISession createSession(String packageName, in ISessionCallback cb, String tag); + List<IBinder> getSessions(in ComponentName compName); }
\ No newline at end of file diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java index 4ee67d1..c07229d 100644 --- a/media/java/android/media/session/MediaSessionLegacyHelper.java +++ b/media/java/android/media/session/MediaSessionLegacyHelper.java @@ -43,7 +43,8 @@ public class MediaSessionLegacyHelper { private Handler mHandler = new Handler(Looper.getMainLooper()); // The legacy APIs use PendingIntents to register/unregister media button // receivers and these are associated with RCC. - private ArrayMap<PendingIntent, SessionHolder> mSessions = new ArrayMap<PendingIntent, SessionHolder>(); + private ArrayMap<PendingIntent, SessionHolder> mSessions + = new ArrayMap<PendingIntent, SessionHolder>(); private MediaSessionLegacyHelper(Context context) { mSessionManager = (SessionManager) context @@ -78,6 +79,8 @@ public class MediaSessionLegacyHelper { } performer.addListener(listener, mHandler); holder.mRccListener = listener; + holder.mFlags |= Session.FLAG_HANDLES_TRANSPORT_CONTROLS; + holder.mSession.setFlags(holder.mFlags); holder.update(); } @@ -86,6 +89,8 @@ public class MediaSessionLegacyHelper { if (holder != null && holder.mRccListener != null) { holder.mSession.getTransportPerformer().removeListener(holder.mRccListener); holder.mRccListener = null; + holder.mFlags &= ~Session.FLAG_HANDLES_TRANSPORT_CONTROLS; + holder.mSession.setFlags(holder.mFlags); holder.update(); } } @@ -98,6 +103,8 @@ public class MediaSessionLegacyHelper { return; } holder.mMediaButtonListener = new MediaButtonListener(pi, context); + holder.mFlags |= Session.FLAG_HANDLES_MEDIA_BUTTONS; + holder.mSession.setFlags(holder.mFlags); holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler); } @@ -105,6 +112,9 @@ public class MediaSessionLegacyHelper { SessionHolder holder = getHolder(pi, false); if (holder != null && holder.mMediaButtonListener != null) { holder.mSession.getTransportPerformer().removeListener(holder.mMediaButtonListener); + holder.mFlags &= ~Session.FLAG_HANDLES_MEDIA_BUTTONS; + holder.mSession.setFlags(holder.mFlags); + holder.mMediaButtonListener = null; holder.update(); } } @@ -113,8 +123,7 @@ public class MediaSessionLegacyHelper { SessionHolder holder = mSessions.get(pi); if (holder == null && createIfMissing) { Session session = mSessionManager.createSession(TAG); - session.setTransportPerformerEnabled(); - session.publish(); + session.setActive(true); holder = new SessionHolder(session, pi); mSessions.put(pi, holder); } @@ -193,6 +202,7 @@ public class MediaSessionLegacyHelper { public final PendingIntent mPi; public MediaButtonListener mMediaButtonListener; public TransportPerformer.Listener mRccListener; + public int mFlags; public SessionHolder(Session session, PendingIntent pi) { mSession = session; diff --git a/media/java/android/media/session/Session.java b/media/java/android/media/session/Session.java index 8ccd788..194679e7 100644 --- a/media/java/android/media/session/Session.java +++ b/media/java/android/media/session/Session.java @@ -45,12 +45,13 @@ import java.util.List; * media to multiple routes or to provide finer grain controls of media. * <p> * A MediaSession is created by calling - * {@link SessionManager#createSession(String)}. Once a session is created - * apps that have the MEDIA_CONTENT_CONTROL permission can interact with the - * session through {@link SessionManager#getActiveSessions()}. The owner of - * the session may also use {@link #getSessionToken()} to allow apps without - * this permission to create a {@link SessionController} to interact with this - * session. + * {@link SessionManager#createSession(String)}. Once a session is created apps + * that have the MEDIA_CONTENT_CONTROL permission can interact with the session + * through + * {@link SessionManager#getActiveSessions(android.content.ComponentName)}. The + * owner of the session may also use {@link #getSessionToken()} to allow apps + * without this permission to create a {@link SessionController} to interact + * with this session. * <p> * To receive commands, media keys, and other events a Callback must be set with * {@link #addCallback(Callback)}. @@ -63,6 +64,28 @@ import java.util.List; public final class Session { private static final String TAG = "Session"; + /** + * Set this flag on the session to indicate that it can handle media button + * events. + */ + public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0; + + /** + * Set this flag on the session to indicate that it handles commands through + * the {@link TransportPerformer}. The performer can be retrieved by calling + * {@link #getTransportPerformer()}. + */ + public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1; + + /** + * System only flag for a session that needs to have priority over all other + * sessions. This flag ensures this session will receive media button events + * regardless of the current ordering in the system. + * + * @hide + */ + public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16; + private static final int MSG_MEDIA_BUTTON = 1; private static final int MSG_COMMAND = 2; private static final int MSG_ROUTE_CHANGE = 3; @@ -86,7 +109,7 @@ public final class Session { private TransportPerformer mPerformer; private Route mRoute; - private boolean mPublished = false;; + private boolean mActive = false;; /** * @hide @@ -101,6 +124,7 @@ public final class Session { throw new RuntimeException("Dead object in MediaSessionController constructor: ", e); } mSessionToken = new SessionToken(controllerBinder); + mPerformer = new TransportPerformer(mBinder); } /** @@ -148,56 +172,57 @@ public final class Session { } /** - * Start using a TransportPerformer with this media session. This must be - * called before calling publish and cannot be called more than once. - * Calling this will allow MediaControllers to retrieve a - * TransportController. + * Retrieves the {@link TransportPerformer} for this session. To receive + * commands through the performer you must also set the + * {@link #FLAG_HANDLES_TRANSPORT_CONTROLS} flag using + * {@link #setFlags(int)}. * - * @see TransportController - * @return The TransportPerformer created for this session + * @return The performer associated with this session. */ - public TransportPerformer setTransportPerformerEnabled() { - if (mPerformer != null) { - throw new IllegalStateException("setTransportPerformer can only be called once."); - } - if (mPublished) { - throw new IllegalStateException("setTransportPerformer cannot be called after publish"); - } - - mPerformer = new TransportPerformer(mBinder); - try { - mBinder.setTransportPerformerEnabled(); - } catch (RemoteException e) { - Log.wtf(TAG, "Failure in setTransportPerformerEnabled.", e); - } + public TransportPerformer getTransportPerformer() { return mPerformer; } /** - * Retrieves the TransportPerformer used by this session. If called before - * {@link #setTransportPerformerEnabled} null will be returned. + * Set any flags for the session. * - * @return The TransportPerformer associated with this session or null + * @param flags The flags to set for this session. */ - public TransportPerformer getTransportPerformer() { - return mPerformer; + public void setFlags(int flags) { + try { + mBinder.setFlags(flags); + } catch (RemoteException e) { + Log.wtf(TAG, "Failure in setFlags.", e); + } } /** - * Call after you have finished setting up the session. This will make it - * available to listeners and begin pushing updates to MediaControllers. - * This can only be called once. + * Set if this session is currently active and ready to receive commands. If + * set to false your session's controller may not be discoverable. You must + * set the session to active before it can start receiving media button + * events or transport commands. + * + * @param active Whether this session is active or not. */ - public void publish() { - if (mPublished) { - throw new RuntimeException("publish() may only be called once."); + public void setActive(boolean active) { + if (mActive == active) { + return; } try { - mBinder.publish(); + mBinder.setActive(active); + mActive = active; } catch (RemoteException e) { - Log.wtf(TAG, "Failure in publish.", e); + Log.wtf(TAG, "Failure in setActive.", e); } - mPublished = true; + } + + /** + * Get the current active state of this session. + * + * @return True if the session is active, false otherwise. + */ + public boolean isActive() { + return mActive; } /** diff --git a/media/java/android/media/session/SessionManager.java b/media/java/android/media/session/SessionManager.java index 15bf0e3..fd022fc 100644 --- a/media/java/android/media/session/SessionManager.java +++ b/media/java/android/media/session/SessionManager.java @@ -16,11 +16,13 @@ package android.media.session; +import android.content.ComponentName; import android.content.Context; import android.media.session.ISessionManager; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.service.notification.NotificationListenerService; import android.util.Log; import java.util.ArrayList; @@ -79,12 +81,27 @@ public final class SessionManager { /** * Get a list of controllers for all ongoing sessions. This requires the * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by - * the calling app. + * the calling app. You may also retrieve this list if your app is an + * enabled notification listener using the + * {@link NotificationListenerService} APIs, in which case you must pass the + * {@link ComponentName} of your enabled listener. * - * @return a list of controllers for ongoing sessions + * @param notificationListener The enabled notification listener component. + * May be null. + * @return A list of controllers for ongoing sessions */ - public List<SessionController> getActiveSessions() { - // TODO - return new ArrayList<SessionController>(); + public List<SessionController> getActiveSessions(ComponentName notificationListener) { + ArrayList<SessionController> controllers = new ArrayList<SessionController>(); + try { + List<IBinder> binders = mService.getSessions(notificationListener); + for (int i = binders.size() - 1; i >= 0; i--) { + SessionController controller = SessionController.fromBinder(ISessionController.Stub + .asInterface(binders.get(i))); + controllers.add(controller); + } + } catch (RemoteException e) { + Log.e(TAG, "Failed to get active sessions: ", e); + } + return controllers; } } diff --git a/media/jni/Android.mk b/media/jni/Android.mk index ed98b96..90fe695 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -66,8 +66,6 @@ LOCAL_C_INCLUDES += \ LOCAL_CFLAGS += -LOCAL_LDLIBS := -lpthread - LOCAL_MODULE:= libmedia_jni include $(BUILD_SHARED_LIBRARY) diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index abebd48..6f42057 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -648,7 +648,7 @@ android_media_MediaPlayer_native_init(JNIEnv *env) return; } - clazz = env->FindClass("android/net/ProxyProperties"); + clazz = env->FindClass("android/net/ProxyInfo"); if (clazz == NULL) { return; } @@ -660,7 +660,7 @@ android_media_MediaPlayer_native_init(JNIEnv *env) env->GetMethodID(clazz, "getPort", "()I"); fields.proxyConfigGetExclusionList = - env->GetMethodID(clazz, "getExclusionList", "()Ljava/lang/String;"); + env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;"); } static void diff --git a/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png Binary files differnew file mode 100644 index 0000000..c779437 --- /dev/null +++ b/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png Binary files differnew file mode 100644 index 0000000..98ba690 --- /dev/null +++ b/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png Binary files differnew file mode 100644 index 0000000..61947ea --- /dev/null +++ b/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png Binary files differnew file mode 100644 index 0000000..0b563b1 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png Binary files differnew file mode 100644 index 0000000..3600ee6 --- /dev/null +++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png diff --git a/packages/SystemUI/res/layout-sw600dp/heads_up.xml b/packages/SystemUI/res/layout-sw600dp/heads_up.xml index 71f7c21..f7035fe 100644 --- a/packages/SystemUI/res/layout-sw600dp/heads_up.xml +++ b/packages/SystemUI/res/layout-sw600dp/heads_up.xml @@ -25,7 +25,6 @@ android:id="@+id/content_holder" android:layout_height="wrap_content" android:layout_width="@dimen/notification_panel_width" - android:layout_marginStart="@dimen/notification_panel_margin_left" android:background="@drawable/heads_up_window_bg" /> </com.android.systemui.statusbar.policy.HeadsUpNotificationView> diff --git a/packages/SystemUI/res/layout/flip_settings.xml b/packages/SystemUI/res/layout/flip_settings.xml index f3c1b90..28d9625 100644 --- a/packages/SystemUI/res/layout/flip_settings.xml +++ b/packages/SystemUI/res/layout/flip_settings.xml @@ -22,5 +22,4 @@ android:layout_height="wrap_content" android:background="#5f000000" android:animateLayoutChanges="true" - android:visibility="gone" android:columnCount="@integer/quick_settings_num_columns" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml index 3a58b84..e4954e7 100644 --- a/packages/SystemUI/res/layout/heads_up.xml +++ b/packages/SystemUI/res/layout/heads_up.xml @@ -22,6 +22,5 @@ android:layout_height="wrap_content" android:layout_width="@dimen/notification_panel_width" android:id="@+id/content_holder" - android:layout_marginStart="@dimen/notification_panel_margin_left" android:background="@drawable/notification_panel_bg" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index ec5acba..194829d 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -55,4 +55,14 @@ android:textStyle="italic" android:textAppearance="?android:attr/textAppearanceMedium"/> + <ImageView + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_gravity="bottom|center_horizontal" + android:src="@drawable/ic_lock_24dp" + android:scaleType="center" + android:alpha="0.7" + android:layerType="hardware" + android:tint="#ffffffff"/> + </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 761ad42..3267c36 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -23,9 +23,7 @@ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/notification_panel" android:layout_width="0dp" - android:layout_height="wrap_content" - android:paddingTop="@dimen/notification_panel_padding_top" - android:layout_marginStart="@dimen/notification_panel_margin_left" + android:layout_height="match_parent" > <include @@ -36,15 +34,6 @@ android:layout_gravity="bottom" /> - <include - layout="@layout/status_bar_flip_button" - android:id="@+id/keyguard_flipper" - android:layout_width="50dp" - android:layout_height="50dp" - android:layout_gravity="right|top" - android:layout_marginTop="@dimen/status_bar_height" - android:visibility="gone" /> - <com.android.keyguard.CarrierText android:id="@+id/keyguard_carrier_text" android:layout_width="wrap_content" @@ -54,11 +43,6 @@ android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceMedium" /> - <include layout="@layout/status_bar_expanded_header" - android:layout_width="match_parent" - android:layout_height="@dimen/notification_panel_header_height" - /> - <include layout="@layout/keyguard_status_view" android:layout_height="wrap_content" @@ -74,27 +58,54 @@ android:visibility="gone" /> - <FrameLayout + <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer android:id="@+id/notification_container_parent" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/close_handle_underlap" - > - <include - layout="@layout/flip_settings" - android:layout_marginTop="@dimen/notification_panel_header_height" + android:clipToPadding="false" + android:clipChildren="false"> + + <com.android.systemui.statusbar.phone.ObservableScrollView + android:id="@+id/scroll_view" android:layout_width="match_parent" - android:layout_height="wrap_content" - /> + android:layout_height="match_parent" + android:visibility="invisible" + android:scrollbars="none" + android:fillViewport="true"> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + <include + layout="@layout/flip_settings" + android:layout_marginTop="@dimen/status_bar_header_height_expanded" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <!-- A view to reserve space for the collapsed stack --> + <View + android:layout_height="@dimen/collapsed_stack_height" + android:layout_width="match_parent"/> + </LinearLayout> + </com.android.systemui.statusbar.phone.ObservableScrollView> + <com.android.systemui.statusbar.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller" android:layout_width="match_parent" - android:layout_height="wrap_content" - /> - </FrameLayout> + android:layout_height="match_parent" + android:layout_marginBottom="@dimen/close_handle_underlap"/> + + </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> + + + <include layout="@layout/status_bar_expanded_header" + android:layout_width="match_parent" + android:layout_height="@dimen/status_bar_header_height" + /> <include layout="@layout/keyguard_bottom_area" android:visibility="gone" /> + </com.android.systemui.statusbar.phone.NotificationPanelView><!-- end of sliding panel --> diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml index 8975728..460dd4b 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml @@ -15,21 +15,30 @@ ** limitations under the License. --> -<LinearLayout +<!-- Extends RelativeLayout --> +<com.android.systemui.statusbar.phone.StatusBarHeaderView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/header" android:layout_width="match_parent" - android:layout_height="@dimen/notification_panel_header_height" - android:background="@drawable/notification_header_bg" + android:layout_height="@dimen/status_bar_header_height" android:orientation="horizontal" android:gravity="center_vertical" android:baselineAligned="false" > + + <View + android:id="@+id/background" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/notification_header_bg" + android:clickable="true" + /> <RelativeLayout android:id="@+id/datetime" android:layout_width="wrap_content" android:layout_height="match_parent" + android:layout_gravity="start" android:paddingStart="8dp" android:paddingEnd="8dp" android:background="@drawable/ic_notify_button_bg" @@ -55,12 +64,6 @@ /> </RelativeLayout> - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1" - /> - <TextView android:id="@+id/header_debug_info" android:visibility="invisible" @@ -74,18 +77,22 @@ android:padding="2dp" /> + <include layout="@layout/status_bar_flip_button" + android:id="@+id/header_flipper" + android:layout_width="50dp" + android:layout_height="50dp" + android:layout_alignParentEnd="true"/> + <ImageView android:id="@+id/clear_all_button" android:layout_width="50dp" android:layout_height="50dp" + android:layout_toStartOf="@id/header_flipper" android:scaleType="center" android:src="@drawable/ic_notify_clear" android:background="@drawable/ic_notify_button_bg" android:contentDescription="@string/accessibility_clear_all" /> - <include layout="@layout/status_bar_flip_button" - android:id="@+id/header_flipper" - android:layout_width="50dp" - android:layout_height="50dp" - android:layout_marginStart="12dp" /> -</LinearLayout> + + +</com.android.systemui.statusbar.phone.StatusBarHeaderView> diff --git a/packages/SystemUI/res/layout/status_bar_flip_button.xml b/packages/SystemUI/res/layout/status_bar_flip_button.xml index b7dff8c..f4d7033 100644 --- a/packages/SystemUI/res/layout/status_bar_flip_button.xml +++ b/packages/SystemUI/res/layout/status_bar_flip_button.xml @@ -15,22 +15,11 @@ ~ limitations under the License --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="50dp" - android:layout_height="50dp"> - <ImageView android:id="@+id/settings_button" - android:layout_width="50dp" - android:layout_height="50dp" - android:scaleType="center" - android:src="@drawable/ic_notify_settings" - android:background="@drawable/ic_notify_button_bg" - android:contentDescription="@string/accessibility_desc_quick_settings" /> - <ImageView android:id="@+id/notification_button" - android:layout_width="50dp" - android:layout_height="50dp" - android:scaleType="center" - android:src="@drawable/ic_notifications" - android:background="@drawable/ic_notify_button_bg" - android:visibility="gone" - android:contentDescription="@string/accessibility_notifications_button" /> -</FrameLayout>
\ No newline at end of file +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/settings_button" + android:layout_width="50dp" + android:layout_height="50dp" + android:scaleType="center" + android:src="@drawable/ic_notify_quicksettings" + android:background="@drawable/ic_notify_button_bg" + android:contentDescription="@string/accessibility_desc_quick_settings"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml index 2e08bff..30eedee 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml @@ -18,7 +18,7 @@ <com.android.systemui.statusbar.NotificationOverflowContainer xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="32dp" + android:layout_height="40dp" android:focusable="true" android:clickable="true" > diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml index 61d43d7..f9b022c 100644 --- a/packages/SystemUI/res/layout/super_status_bar.xml +++ b/packages/SystemUI/res/layout/super_status_bar.xml @@ -33,11 +33,10 @@ <com.android.systemui.statusbar.phone.PanelHolder android:id="@+id/panel_holder" android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginTop="@dimen/panel_holder_padding_top"> + android:layout_height="match_parent" > <include layout="@layout/status_bar_expanded" android:layout_width="@dimen/notification_panel_width" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:layout_gravity="start|top" /> </com.android.systemui.statusbar.phone.PanelHolder> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index d8a3114..8ec48c8 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Onlangse programme"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Deursoek"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Knoppie vir wissel van invoermetode."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Versoenbaarheid-zoem se knoppie."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoem kleiner na groter skerm."</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 9d1e036..7aaaaf3 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"የቅርብ ጊዜ መተግበሪያዎች"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ፈልግ"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"ካሜራ"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"የግቤት ስልት አዝራር ቀይር"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 22a032a..1c06615 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"التطبيقات الحديثة"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"بحث"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"الكاميرا"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"الهاتف"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"زر تبديل طريقة الإدخال."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"زر تكبير/تصغير للتوافق."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"استخدام التكبير/التصغير لتحويل شاشة صغيرة إلى شاشة أكبر"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 00ac707..febcab6 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Скорошни приложения"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Търсене"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Бутон за превключване на метода на въвеждане."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Бутон за промяна на мащаба с цел съвместимост."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Промяна на мащаба на екрана от по-малък до по-голям."</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index bfad5bc..658ece0 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicacions recents"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Cerca"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Càmera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botó de canvi del mètode d\'entrada."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Amplia menys com més gran sigui la pantalla."</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 94ba5c7..c7d2703 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikace"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Hledat"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačítko přepnutí metody zadávání"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačítko úpravy velikosti z důvodu kompatibility"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zvětšit menší obrázek na větší obrazovku."</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index df442ab..c740051 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Seneste apps"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Søg"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Skift indtastningsmetode-knappen."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knap for kompatibilitetszoom."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom mindre til større skærm."</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 451476c..40ecc49 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Kürzlich geöffnete Apps"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Suchen"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefonnummer"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Schaltfläche zum Ändern der Eingabemethode"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Schaltfläche für Kompatibilitätszoom"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom auf einen größeren Bildschirm"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 6dcffcf..0e98ce0 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Πρόσφατες εφαρμογές"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Αναζήτηση"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Φωτογραφική μηχανή"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Τηλέφωνο"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Κουμπί εναλλαγής μεθόδου εισόδου"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Κουμπί εστίασης συμβατότητας."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Ζουμ από μικρότερη σε μεγαλύτερη οθόνη."</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 91a334a..f4098b7 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Recent apps"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Search"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Camera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Phone"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Switch input method button."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 91a334a..f4098b7 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Recent apps"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Search"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Camera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Phone"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Switch input method button."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 2150cbe..51198e6 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaciones recientes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Buscar"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Cámara"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botón Cambiar método de entrada"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 3424166..1b73e18 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaciones recientes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Buscar"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Cámara"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Teléfono"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botón Cambiar método de entrada"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string> diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml index 8dbf9af..0f95c60 100644 --- a/packages/SystemUI/res/values-et-rEE/strings.xml +++ b/packages/SystemUI/res/values-et-rEE/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Hiljutised rakendused"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Otsing"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kaamera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Sisestusmeetodi vahetamise nupp."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Sobivussuumi nupp."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Suumi suuremale ekraanile vähem."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index b2ac990..f36d04b 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"برنامههای اخیر"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"جستجو"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"دوربین"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"کلید تغییر روش ورود متن."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"دکمه بزرگنمایی سازگار."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"بزرگنمایی از صفحههای کوچک تا بزرگ."</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index af0df4d..4ae3851 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Viimeaikaiset sovellukset"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Haku"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Syöttötavan vaihtopainike."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Yhteensopivuuszoomaus-painike."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoomaa pienemmältä suuremmalle ruudulle."</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index f01c99a..c55a5fc 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 5efbd2f..1121918 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index e6d38ef..f9e6aad 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"हाल ही के ऐप्स"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"खोजें"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"कैमरा"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"फ़ोन"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"इनपुट पद्धति बटन स्विच करें."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"संगतता ज़ूम बटन."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"छोटी से बड़ी स्क्रीन पर ज़ूम करें."</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index b777b8f..435f701 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Nedavne aplikacije"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Pretraži"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparat"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Gumb za promjenu načina unosa."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Gumb za kompatibilnost zumiranja."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zumiranje manjeg zaslona na veći."</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 10732fe..7685477 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Legújabb alkalmazás"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Keresés"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Beviteli mód váltása gomb."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kompatibilitási zoom gomb."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kicsinyítsen a nagyobb képernyőhöz."</string> diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index f1c4869..7a8ab10 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Վերջին ծրագրերը"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Որոնել"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Ֆոտոխցիկ"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Միացնել մուտքագրման եղանակի կոճակը:"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Համատեղելիության խոշորացման կոճակը:"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Դիտափոխել փոքրից ավելի մեծ էկրան:"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index f0b0713..cdb0816 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Apl terbaru"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Telusuri"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telepon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tombol beralih metode masukan."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tombol perbesar/perkecil kompatibilitas."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Perbesar dari layar kecil ke besar."</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 6a57682..401dbce 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Applicazioni recenti"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Cerca"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotocamera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefono"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Pulsante per cambiare metodo di immissione."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Pulsante zoom compatibilità."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom inferiore per schermo più grande."</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 3e87b76..08ffc54 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"אפליקציות אחרונות"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"חפש"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"מצלמה"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"לחצן החלפת שיטת קלט."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"לחצן מרחק מתצוגה של תאימות."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"שנה מרחק מתצוגה של מסך קטן לגדול יותר."</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 1edb630..2caa069 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"最近使ったアプリ"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"検索"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"カメラ"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"入力方法の切り替えボタン。"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"互換ズームボタン。"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"小さい画面から大きい画面に拡大。"</string> diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index 9d4344a..62e40d0 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"ბოლოს გამოყენებული აპები"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ძიება"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"კამერა"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"შეყვანის მეთოდის გადართვის ღილაკი."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"თავსებადი მასშტაბირების ღილაკი."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"შეცვალეთ პატარა ეკრანი უფრო დიდით."</string> diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml index bb2d3b9..35a6917 100644 --- a/packages/SystemUI/res/values-km-rKH/strings.xml +++ b/packages/SystemUI/res/values-km-rKH/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"កម្មវិធីថ្មីៗ"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ស្វែងរក"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"ម៉ាស៊ីនថត"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ប្ដូរប៊ូតុងវិធីសាស្ត្របញ្ចូល។"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុងពង្រីកត្រូវគ្នា។"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ពង្រីក/បង្រួមអេក្រង់ពីទៅធំ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 83ffa9f..d7cd13b 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"최근에 사용한 앱"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"검색"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"카메라"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"입력 방법 버튼을 전환합니다."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"호환성 확대/축소 버튼입니다."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"작은 화면을 큰 화면으로 확대합니다."</string> diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml index 9a3d483..991fb66 100644 --- a/packages/SystemUI/res/values-lo-rLA/strings.xml +++ b/packages/SystemUI/res/values-lo-rLA/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"ແອັບຯຫຼ້າສຸດ"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ຊອກຫາ"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"ກ້ອງ"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"ໂທລະສັບ"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ປຸ່ມສະລັບຮູບແບບການປ້ອນຂໍ້ມູນ."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ປຸ່ມຊູມທີ່ໃຊ້ຮ່ວມກັນໄດ້."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ຊູມຈໍນ້ອຍໄປເປັນຈໍຂະຫນາດໃຫຍ່."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 379cfce..87087b4 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Naujausios programos"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Ieškoti"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparatas"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefonas"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Perjungti įvesties metodo mygtuką."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Suderinamumo priartinimo mygtukas."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Padidinti ekraną."</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index ca07095..04571e7 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Nesen izmantotās lietotnes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Meklēt"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Tālruņa numurs"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Ievades metodes maiņas poga."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Saderības tālummaiņas poga."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Veikt tālummaiņu no mazāka ekrāna uz lielāku."</string> diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml index 4545301..3e7b660 100644 --- a/packages/SystemUI/res/values-mn-rMN/strings.xml +++ b/packages/SystemUI/res/values-mn-rMN/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Сүүлийн апп"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Хайх"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Камер"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Оруулах аргыг сэлгэх товч."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Тохиромжтой өсгөх товч."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Жижгээс том дэлгэцрүү өсгөх."</string> diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index 587b84f..af50786 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplikasi terbaharu"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Cari"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Butang tukar kaedah input."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Butang zum keserasian."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Skrin zum lebih kecil kepada lebih besar."</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 001e733..9fa68b9 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Nylige apper"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Søk"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefonnummer"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bytt knapp for inndatametode."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Zoomknapp for kompatibilitet."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom fra mindre til større skjerm."</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 8a6e33b..8cf16bf 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Recente apps"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Zoeken"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Camera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefoon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Knop voor wijzigen invoermethode."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knop voor compatibiliteitszoom."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kleiner scherm uitzoomen naar groter scherm."</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 4862d1b..e51341d 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Ostatnie aplikacje"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Szukaj"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Aparat"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Przycisk przełączania metody wprowadzania."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Przycisk powiększenia na potrzeby zgodności."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Powiększa mniejszy ekran do większego."</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 012283a..11843b6 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicações recentes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Pesquisar"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Câmara"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telemóvel"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Alternar botão de método de introdução."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão zoom de compatibilidade."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom menor para ecrã maior."</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 0978d18..162d763 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicativos recentes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Pesquisar"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Câmera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Alterar botão do método de entrada."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string> diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml index 25f643f..9f82651 100644 --- a/packages/SystemUI/res/values-rm/strings.xml +++ b/packages/SystemUI/res/values-rm/strings.xml @@ -126,6 +126,8 @@ <skip /> <!-- no translation found for accessibility_camera_button (8064671582820358152) --> <skip /> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <!-- no translation found for accessibility_ime_switch_button (5032926134740456424) --> <skip /> <!-- no translation found for accessibility_compatibility_zoom_button (8461115318742350699) --> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 5c9764a..75ba35b 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaţii recente"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Căutați"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Cameră foto"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Buton pentru comutarea metodei de introducere."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Buton zoom pentru compatibilitate."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Faceţi zoom de la o imagine mai mică la una mai mare."</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 976a793..6bdc6f4 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Недавние приложения"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Поиск"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Кнопка переключения способа ввода."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 1224c3a..12c90ec 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikácie"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Hľadať"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačidlo prepnutia metódy vstupu."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačidlo úpravy veľkosti z dôvodu kompatibility."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zväčšiť menší obrázok na väčšiu obrazovku."</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 10011bc..48d7727 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Nedavni programi"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Iskanje"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparat"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Gumb za preklop načina vnosa."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Gumb povečave za združljivost."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Povečava manjšega na večji zaslon."</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 0c6a939..dba27a1 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Недавне апликације"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Претражите"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Дугме Промени метод уноса."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Дугме Зум компатибилности."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Зумирање са мањег на већи екран."</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index d8bd1b1..0d58ed3 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Senaste apparna"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Sök"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Mobil"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Knapp för byte av inmatningsmetod."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knapp för kompatibilitetszoom."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zooma mindre skärm till större."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index d308b51..b626f78 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -76,6 +76,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Programu za hivi karibuni"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Tafuta"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Swichi kitufe cha mbinu ingizi."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kichupo cha kukuza kwa utangamanifu"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kuza kidogo kwa skrini kubwa."</string> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 92e3885..7372181 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -19,10 +19,6 @@ <!-- The width of the notification panel window: 446 + 16 + 16 (padding in the bg drawable) --> <dimen name="notification_panel_width">478dp</dimen> - <!-- Layout parameters for the notification panel --> - <dimen name="notification_panel_margin_bottom">192dp</dimen> - <dimen name="notification_panel_margin_left">16dp</dimen> - <!-- Gravity for the notification panel --> <!-- 0x31 = top|center_horizontal --> <integer name="notification_panel_layout_gravity">0x31</integer> @@ -43,9 +39,6 @@ <dimen name="status_bar_recents_thumbnail_width">200dp</dimen> <dimen name="status_bar_recents_thumbnail_height">177dp</dimen> - <!-- On tablets, panels drop from the statusbar instead of overlapping it. --> - <dimen name="panel_holder_padding_top">@*android:dimen/status_bar_height</dimen> - <!-- Minimum fraction of the screen that should be taken up by the notification panel. --> <item type="dimen" name="notification_panel_min_height_frac">40%</item> diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml index b1fc00a..a1c5b66 100644 --- a/packages/SystemUI/res/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp/dimens.xml @@ -24,8 +24,6 @@ <!-- The width of the ticker, including the icon --> <dimen name="notification_ticker_width">360dp</dimen> - <!-- Status bar panel bottom offset (height of status bar - overlap) --> - <dimen name="status_bar_panel_bottom_offset">36dp</dimen> <!-- gap on either side of status bar notification icons --> <dimen name="status_bar_icon_padding">1dp</dimen> <!-- The width of the notification panel window --> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 7929a30..6cb4a48 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"แอปพลิเคชันล่าสุด"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ค้นหา"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"กล้องถ่ายรูป"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ปุ่มสลับวิธีการป้อนข้อมูล"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ซูมหน้าจอให้มีขนาดใหญ่ขึ้น"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 92473a4..17e720e 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Kamakailang apps"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Hanapin"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Camera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Ilipat ang button na pamamaraan ng pag-input."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Button ng zoom ng pagiging tugma."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Mag-zoom nang mas maliit sa mas malaking screen."</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 9837ff8..1bf033f 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Son uygulamalar"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Ara"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Giriş yöntemini değiştirme düğmesi."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyumluluk zum düğmesi."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha büyük ekrana daha küçük yakınlaştır."</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 534f520..a5284ae 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Останні програми"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Пошук"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Номер телефону"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Кнопка перемикання методу введення."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабування сумісності."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Збільшення екрана."</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 56fc89c..8df2449 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Ứng dụng gần đây"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Tìm kiếm"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Máy ảnh"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Nút chuyển phương thức nhập."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Nút thu phóng khả năng tương thích."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Thu phóng màn hình lớn hơn hoặc nhỏ hơn."</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 442fdb7..8643ca8 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"最近运行的应用"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"搜索"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"相机"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"输入法切换按钮。"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"兼容性缩放按钮。"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"将小屏幕的图片放大在较大屏幕上显示。"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 54a3b1e..9d93094 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"最近使用的應用程式"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"搜尋"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"相機"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"切換輸入法按鈕。"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index fc74f53..07b7841 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -78,6 +78,8 @@ <string name="accessibility_recent" msgid="8571350598987952883">"最近使用的應用程式"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"搜尋"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"相機"</string> + <!-- no translation found for accessibility_phone_button (6738112589538563574) --> + <skip /> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"切換輸入法按鈕。"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index dfe8838..48a1ace 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -78,6 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Izinhlelo zokusebenza zakamuva"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Sesha"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Ikhamela"</string> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Ifoni"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Vula indlela yokungena yenkinobho"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Inkinobho evumelekile yokusondeza"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Sondeza kancane esikrinini esikhudlwana"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 04289da..c0376f0 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -99,9 +99,6 @@ <integer name="blinds_pop_duration_ms">10</integer> - <!-- The device supports quick settings. --> - <bool name="config_hasQuickSettings">true</bool> - <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? --> <bool name="config_show4GForLTE">true</bool> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ff5bdab..c6fdc16 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -152,21 +152,11 @@ <!-- Amount of close_handle that will NOT overlap the notification list --> <dimen name="close_handle_underlap">32dp</dimen> - <!-- Height of the notification panel header bar --> - <dimen name="notification_panel_header_height">48dp</dimen> + <!-- Height of the status bar header bar --> + <dimen name="status_bar_header_height">48dp</dimen> - <!-- Extra space above the panel --> - <dimen name="notification_panel_padding_top">0dp</dimen> - - <!-- Extra space above the clock in the panel --> - <dimen name="notification_panel_header_padding_top">0dp</dimen> - - <!-- Extra space above the panel holder --> - <dimen name="panel_holder_padding_top">0dp</dimen> - - <!-- Layout parameters for the notification panel --> - <dimen name="notification_panel_margin_bottom">0dp</dimen> - <dimen name="notification_panel_margin_left">0dp</dimen> + <!-- Height of the status bar header bar when expanded --> + <dimen name="status_bar_header_height_expanded">144dp</dimen> <!-- Gravity for the notification panel --> <!-- 0x37 = fill_horizontal|top --> @@ -264,6 +254,9 @@ <!-- The padding between the individual notification cards. --> <dimen name="notification_padding">3dp</dimen> + <!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) --> + <dimen name="collapsed_stack_height">94dp</dimen> + <!-- Width of the zen mode interstitial dialog. --> <dimen name="zen_mode_dialog_width">320dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml new file mode 100644 index 0000000..6418930 --- /dev/null +++ b/packages/SystemUI/res/values/ids.xml @@ -0,0 +1,38 @@ +<?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 + --> + +<resources> + <item type="id" name="translation_y_animator_tag"/> + <item type="id" name="translation_z_animator_tag"/> + <item type="id" name="scale_animator_tag"/> + <item type="id" name="alpha_animator_tag"/> + <item type="id" name="top_inset_animator_tag"/> + <item type="id" name="height_animator_tag"/> + <item type="id" name="translation_y_animator_end_value_tag"/> + <item type="id" name="translation_z_animator_end_value_tag"/> + <item type="id" name="scale_animator_end_value_tag"/> + <item type="id" name="alpha_animator_end_value_tag"/> + <item type="id" name="top_inset_animator_end_value_tag"/> + <item type="id" name="height_animator_end_value_tag"/> + <item type="id" name="translation_y_animator_start_value_tag"/> + <item type="id" name="translation_z_animator_start_value_tag"/> + <item type="id" name="scale_animator_start_value_tag"/> + <item type="id" name="alpha_animator_start_value_tag"/> + <item type="id" name="top_inset_animator_start_value_tag"/> + <item type="id" name="height_animator_start_value_tag"/> +</resources> + diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index d38d828..6387a92 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -322,6 +322,7 @@ public class SwipeHelper implements Gefingerpoken { anim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animator) { updateAlphaFromOffset(animView, canAnimViewBeDismissed); + mCallback.onChildSnappedBack(animView); } }); anim.start(); @@ -407,5 +408,7 @@ public class SwipeHelper implements Gefingerpoken { void onChildDismissed(View v); void onDragCancelled(View v); + + void onChildSnappedBack(View animView); } } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java index 35c824b..0759b8e 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java @@ -217,6 +217,10 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView public void onDragCancelled(View v) { } + @Override + public void onChildSnappedBack(View animView) { + } + public View getChildAtPosition(MotionEvent ev) { final float x = ev.getX() + getScrollX(); final float y = ev.getY() + getScrollY(); diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java index 297fe0d..c2dde6a 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java @@ -225,6 +225,10 @@ public class RecentsVerticalScrollView extends ScrollView public void onDragCancelled(View v) { } + @Override + public void onChildSnappedBack(View animView) { + } + public View getChildAtPosition(MotionEvent ev) { final float x = ev.getX() + getScrollX(); final float y = ev.getY() + getScrollY(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 1c88ea7..91df9ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -38,6 +38,7 @@ import com.android.internal.R; public abstract class ActivatableNotificationView extends ExpandableOutlineView { private static final long DOUBLETAP_TIMEOUT_MS = 1000; + private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220; private boolean mDimmed; @@ -174,12 +175,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private void makeInactive() { if (mActivated) { // Make sure that we clear the hotspot from the center. - mBackgroundDimmed.setHotspot(0, getWidth() / 2, getActualHeight() / 2); - mBackgroundDimmed.removeHotspot(0); + if (mBackgroundDimmed != null) { + mBackgroundDimmed.setHotspot(0, getWidth() / 2, getActualHeight() / 2); + mBackgroundDimmed.removeHotspot(0); + } mActivated = false; } if (mOnActivatedListener != null) { - mOnActivatedListener.onReset(this); + mOnActivatedListener.onActivationReset(this); } removeCallbacks(mTapTimeoutRunnable); } @@ -189,12 +192,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView && Math.abs(event.getY() - mDownY) < mTouchSlop; } - /** - * Sets the notification as dimmed, meaning that it will appear in a more gray variant. - * - * @param dimmed Whether the notification should be dimmed. - * @param fade Whether an animation should be played to change the state. - */ public void setDimmed(boolean dimmed, boolean fade) { if (mDimmed != dimmed) { mDimmed = dimmed; @@ -226,7 +223,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } int startAlpha = mDimmed ? 255 : 0; int endAlpha = mDimmed ? 0 : 255; - int duration = NotificationActivator.ANIMATION_LENGTH_MS; + int duration = BACKGROUND_ANIMATION_LENGTH_MS; // Check whether there is already a background animation running. if (mBackgroundAnimator != null) { startAlpha = (Integer) mBackgroundAnimator.getAnimatedValue(); @@ -313,8 +310,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } @Override - public void setActualHeight(int actualHeight) { - super.setActualHeight(actualHeight); + public void setActualHeight(int actualHeight, boolean notifyListeners) { + super.setActualHeight(actualHeight, notifyListeners); invalidate(); setPivotY(actualHeight / 2); } @@ -331,6 +328,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView public interface OnActivatedListener { void onActivated(View view); - void onReset(View view); + void onActivationReset(View view); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 2c7464a..898f06e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -70,7 +70,7 @@ import android.widget.TextView; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.util.LegacyNotificationUtil; +import com.android.internal.util.NotificationColorUtil; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SearchPanelView; @@ -143,7 +143,7 @@ public abstract class BaseStatusBar extends SystemUI implements // public mode, private notifications, etc private boolean mLockscreenPublicMode = false; private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); - private LegacyNotificationUtil mLegacyNotificationUtil = LegacyNotificationUtil.getInstance(); + private NotificationColorUtil mNotificationColorUtil = NotificationColorUtil.getInstance(); private UserManager mUserManager; @@ -302,7 +302,7 @@ public abstract class BaseStatusBar extends SystemUI implements ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>(); mCommandQueue = new CommandQueue(this, iconList); - int[] switches = new int[7]; + int[] switches = new int[8]; ArrayList<IBinder> binders = new ArrayList<IBinder>(); try { mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications, @@ -317,7 +317,7 @@ public abstract class BaseStatusBar extends SystemUI implements setSystemUiVisibility(switches[1], 0xffffffff); topAppWindowChanged(switches[2] != 0); // StatusBarManagerService has a back up of IME token and it's restored here. - setImeWindowStatus(binders.get(0), switches[3], switches[4]); + setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[7] != 0); setHardKeyboardStatus(switches[5] != 0, switches[6] != 0); // Set up the initial icon state @@ -852,7 +852,7 @@ public abstract class BaseStatusBar extends SystemUI implements Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic); icon.setImageDrawable(iconDrawable); - if (mLegacyNotificationUtil.isGrayscale(iconDrawable)) { + if (mNotificationColorUtil.isGrayscale(iconDrawable)) { icon.setBackgroundResource( com.android.internal.R.drawable.notification_icon_legacy_bg_inset); } @@ -1029,6 +1029,9 @@ public abstract class BaseStatusBar extends SystemUI implements } protected void addNotificationViews(NotificationData.Entry entry) { + if (entry == null) { + return; + } // Add the expanded view and icon. int pos = mNotificationData.add(entry); if (DEBUG) { @@ -1067,7 +1070,6 @@ public abstract class BaseStatusBar extends SystemUI implements entry.row.setSystemExpanded(top); } } - entry.row.setDimmed(onKeyguard, false /* fade */); boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification); if (onKeyguard && (visibleNotifications >= maxKeyguardNotifications || !showOnKeyguard)) { @@ -1087,48 +1089,11 @@ public abstract class BaseStatusBar extends SystemUI implements if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) { mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE); - mKeyguardIconOverflowContainer.setDimmed(true /* dimmed */, false /* fade */); } else { mKeyguardIconOverflowContainer.setVisibility(View.GONE); } } - @Override - public void onActivated(View view) { - int n = mNotificationData.size(); - for (int i = 0; i < n; i++) { - NotificationData.Entry entry = mNotificationData.get(i); - if (entry.row.getVisibility() != View.GONE) { - if (view == entry.row) { - entry.row.getActivator().activate(); - } else { - entry.row.getActivator().activateInverse(); - } - } - } - if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) { - if (view == mKeyguardIconOverflowContainer) { - mKeyguardIconOverflowContainer.getActivator().activate(); - } else { - mKeyguardIconOverflowContainer.getActivator().activateInverse(); - } - } - } - - @Override - public void onReset(View view) { - int n = mNotificationData.size(); - for (int i = 0; i < n; i++) { - NotificationData.Entry entry = mNotificationData.get(i); - if (entry.row.getVisibility() != View.GONE) { - entry.row.getActivator().reset(); - } - } - if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) { - mKeyguardIconOverflowContainer.getActivator().reset(); - } - } - private boolean shouldShowOnKeyguard(StatusBarNotification sbn) { return sbn.getNotification().priority >= Notification.PRIORITY_LOW; } @@ -1144,7 +1109,6 @@ public abstract class BaseStatusBar extends SystemUI implements protected abstract void updateNotificationIcons(); protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime); protected abstract void updateExpandedViewPos(int expandedPosition); - protected abstract int getExpandedViewMaxHeight(); protected abstract boolean shouldDisableNavbarGestures(); protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index bbbe8fa..5362af5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -65,6 +65,8 @@ public class CommandQueue extends IStatusBar.Stub { public static final int FLAG_EXCLUDE_INPUT_METHODS_PANEL = 1 << 3; public static final int FLAG_EXCLUDE_COMPAT_MODE_PANEL = 1 << 4; + private static final String SHOW_IME_SWITCHER_KEY = "showImeSwitcherKey"; + private StatusBarIconList mList; private Callbacks mCallbacks; private Handler mHandler = new H(); @@ -91,7 +93,8 @@ public class CommandQueue extends IStatusBar.Stub { public void animateExpandSettingsPanel(); public void setSystemUiVisibility(int vis, int mask); public void topAppWindowChanged(boolean visible); - public void setImeWindowStatus(IBinder token, int vis, int backDisposition); + public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + boolean showImeSwitcher); public void setHardKeyboardStatus(boolean available, boolean enabled); public void toggleRecentApps(); public void preloadRecentApps(); @@ -190,11 +193,13 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { + public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { synchronized (mList) { mHandler.removeMessages(MSG_SHOW_IME_BUTTON); - mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token) - .sendToTarget(); + Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token); + m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher); + m.sendToTarget(); } } @@ -298,7 +303,8 @@ public class CommandQueue extends IStatusBar.Stub { mCallbacks.topAppWindowChanged(msg.arg1 != 0); break; case MSG_SHOW_IME_BUTTON: - mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2); + mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2, + msg.getData().getBoolean(SHOW_IME_SWITCHER_KEY, false)); break; case MSG_SET_HARD_KEYBOARD_STATUS: mCallbacks.setHardKeyboardStatus(msg.arg1 != 0, msg.arg2 != 0); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index e471754..5b2ea0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -117,7 +117,7 @@ public class DragDownHelper implements Gefingerpoken { } else { if (mDraggedFarEnough) { mDraggedFarEnough = false; - mOnDragDownListener.onReset(); + mOnDragDownListener.onDragDownReset(); } } return true; @@ -188,7 +188,7 @@ public class DragDownHelper implements Gefingerpoken { cancelExpansion(mStartingChild); } mDraggingDown = false; - mOnDragDownListener.onReset(); + mOnDragDownListener.onDragDownReset(); } private ExpandableView findView(float x, float y) { @@ -200,7 +200,7 @@ public class DragDownHelper implements Gefingerpoken { public interface OnDragDownListener { void onDraggedDown(View startingChild); - void onReset(); + void onDragDownReset(); void onThresholdReached(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index e5512a3..39f2bb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -52,7 +52,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private NotificationContentView mPublicLayout; private NotificationContentView mPrivateLayout; private int mMaxExpandHeight; - private NotificationActivator mActivator; public ExpandableNotificationRow(Context context, AttributeSet attrs) { super(context, attrs); @@ -63,8 +62,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { super.onFinishInflate(); mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic); mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded); - - mActivator = new NotificationActivator(this, this); } @Override @@ -208,23 +205,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { mPrivateLayout.setVisibility(show ? View.GONE : View.VISIBLE); } - /** - * Sets the notification as dimmed, meaning that it will appear in a more gray variant. - */ - @Override - public void setDimmed(boolean dimmed, boolean fade) { - super.setDimmed(dimmed, fade); - mActivator.setDimmed(dimmed, fade); - } - public int getMaxExpandHeight() { return mMaxExpandHeight; } - public NotificationActivator getActivator() { - return mActivator; - } - /** * @return the potential height this view could expand in addition. */ @@ -238,10 +222,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } @Override - public void setActualHeight(int height) { - mPrivateLayout.setActualHeight(height); + public void setActualHeight(int height, boolean notifyListeners) { + mPrivateLayout.setActualHeight(height, notifyListeners); invalidate(); - super.setActualHeight(height); + super.setActualHeight(height, notifyListeners); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java index 43eb5b5..a42c194 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java @@ -33,8 +33,8 @@ public abstract class ExpandableOutlineView extends ExpandableView { } @Override - public void setActualHeight(int actualHeight) { - super.setActualHeight(actualHeight); + public void setActualHeight(int actualHeight, boolean notifyListeners) { + super.setActualHeight(actualHeight, notifyListeners); updateOutline(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 33e9051..281bd2d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -17,11 +17,6 @@ package com.android.systemui.statusbar; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Outline; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.InsetDrawable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -66,10 +61,19 @@ public abstract class ExpandableView extends FrameLayout { /** * Sets the actual height of this notification. This is different than the laid out * {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding. + * + * @param actualHeight The height of this notification. + * @param notifyListeners Whether the listener should be informed about the change. */ - public void setActualHeight(int actualHeight) { + public void setActualHeight(int actualHeight, boolean notifyListeners) { mActualHeight = actualHeight; - notifyHeightChanged(); + if (notifyListeners) { + notifyHeightChanged(); + } + } + + public void setActualHeight(int actualHeight) { + setActualHeight(actualHeight, true); } /** @@ -96,6 +100,15 @@ public abstract class ExpandableView extends FrameLayout { } /** + * Sets the notification as dimmed. The default implementation does nothing. + * + * @param dimmed Whether the notification should be dimmed. + * @param fade Whether an animation should be played to change the state. + */ + public void setDimmed(boolean dimmed, boolean fade) { + } + + /** * @return The desired notification height. */ public int getIntrinsicHeight() { @@ -112,6 +125,10 @@ public abstract class ExpandableView extends FrameLayout { mClipTopAmount = clipTopAmount; } + public int getClipTopAmount() { + return mClipTopAmount; + } + public void setOnHeightChangedListener(OnHeightChangedListener listener) { mOnHeightChangedListener = listener; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java deleted file mode 100644 index a03aeec..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.systemui.statusbar; - -import android.content.Context; -import android.view.View; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -import com.android.systemui.R; - -/** - * A helper class used by both {@link com.android.systemui.statusbar.ExpandableNotificationRow} and - * {@link com.android.systemui.statusbar.NotificationOverflowIconsView} to make a notification look - * active after tapping it once on the Keyguard. - */ -public class NotificationActivator { - - public static final int ANIMATION_LENGTH_MS = 220; - private static final float INVERSE_ALPHA = 0.9f; - private static final float DIMMED_SCALE = 0.95f; - - /** - * Normal state. Notification is fully interactable. - */ - private static final int STATE_NORMAL = 0; - - /** - * Dimmed state. Neutral state when on the lockscreen, with slight transparency and scaled down - * a bit. - */ - private static final int STATE_DIMMED = 1; - - /** - * Activated state. Used after tapping a notification on the lockscreen. Normal transparency and - * normal scale. - */ - private static final int STATE_ACTIVATED = 2; - - /** - * Inverse activated state. Used for the other notifications on the lockscreen when tapping on - * one. - */ - private static final int STATE_ACTIVATED_INVERSE = 3; - - private final View mTargetView; - private final View mHotspotView; - private final Interpolator mFastOutSlowInInterpolator; - private final Interpolator mLinearOutSlowInInterpolator; - private final int mTranslationZ; - - private int mState; - - public NotificationActivator(View targetView, View hotspotView) { - mTargetView = targetView; - mHotspotView = hotspotView; - Context ctx = targetView.getContext(); - mFastOutSlowInInterpolator = - AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in); - mLinearOutSlowInInterpolator = - AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in); - mTranslationZ = - ctx.getResources().getDimensionPixelSize(R.dimen.z_distance_between_notifications); - mTargetView.animate().setDuration(ANIMATION_LENGTH_MS); - } - - public void activateInverse() { - if (mState == STATE_ACTIVATED_INVERSE) { - return; - } - mTargetView.animate().cancel(); - mTargetView.animate().withLayer().alpha(INVERSE_ALPHA); - mState = STATE_ACTIVATED_INVERSE; - } - - public void addHotspot() { - mHotspotView.getBackground().setHotspot( - 0, mHotspotView.getWidth()/2, mHotspotView.getHeight()/2); - } - - public void activate() { - if (mState == STATE_ACTIVATED) { - return; - } - mTargetView.animate().cancel(); - mTargetView.animate() - .setInterpolator(mLinearOutSlowInInterpolator) - .scaleX(1) - .scaleY(1) - .translationZBy(mTranslationZ); - mState = STATE_ACTIVATED; - } - - public void reset() { - if (mState == STATE_DIMMED) { - return; - } - mTargetView.animate().cancel(); - mTargetView.animate() - .setInterpolator(mFastOutSlowInInterpolator) - .scaleX(DIMMED_SCALE) - .scaleY(DIMMED_SCALE) - .translationZBy(-mTranslationZ); - if (mTargetView.getAlpha() != 1.0f) { - mTargetView.animate().withLayer().alpha(1); - } - mState = STATE_DIMMED; - } - - public void setDimmed(boolean dimmed, boolean fade) { - if (dimmed) { - mTargetView.animate().cancel(); - if (fade) { - mTargetView.animate() - .setInterpolator(mFastOutSlowInInterpolator) - .scaleX(DIMMED_SCALE) - .scaleY(DIMMED_SCALE); - } else { - mTargetView.setScaleX(DIMMED_SCALE); - mTargetView.setScaleY(DIMMED_SCALE); - } - mState = STATE_DIMMED; - } else { - mTargetView.animate().cancel(); - if (fade) { - mTargetView.animate() - .setInterpolator(mFastOutSlowInInterpolator) - .scaleX(1) - .scaleY(1); - } else { - mTargetView.animate().cancel(); - mTargetView.setScaleX(1); - mTargetView.setScaleY(1); - } - mState = STATE_NORMAL; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 1f15eaf..9df2701 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -70,8 +70,8 @@ public class NotificationContentView extends ExpandableView { } @Override - public void setActualHeight(int actualHeight) { - super.setActualHeight(actualHeight); + public void setActualHeight(int actualHeight, boolean notifyListeners) { + super.setActualHeight(actualHeight, notifyListeners); selectLayout(); updateClipping(); } @@ -94,10 +94,6 @@ public class NotificationContentView extends ExpandableView { updateClipping(); } - public int getClipTopAmount() { - return mClipTopAmount; - } - private void updateClipping() { mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight); setClipBounds(mClipBounds); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java index e6b5600..864c597 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java @@ -28,14 +28,13 @@ import com.android.systemui.R; public class NotificationOverflowContainer extends ActivatableNotificationView { private NotificationOverflowIconsView mIconsView; - private NotificationActivator mActivator; public NotificationOverflowContainer(Context context, AttributeSet attrs) { super(context, attrs); } @Override - public void setActualHeight(int currentHeight) { + public void setActualHeight(int currentHeight, boolean notifyListeners) { // noop } @@ -54,22 +53,9 @@ public class NotificationOverflowContainer extends ActivatableNotificationView { super.onFinishInflate(); mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view); mIconsView.setMoreText((TextView) findViewById(R.id.more_text)); - - mActivator = new NotificationActivator(this, this); - setDimmed(true, false); - } - - @Override - public void setDimmed(boolean dimmed, boolean fade) { - super.setDimmed(dimmed, fade); - mActivator.setDimmed(dimmed, fade); } public NotificationOverflowIconsView getIconsView() { return mIconsView; } - - public NotificationActivator getActivator() { - return mActivator; - } } 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 627b80f..f63ba9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -16,11 +16,17 @@ package com.android.systemui.statusbar.phone; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; +import android.view.VelocityTracker; import android.view.View; import android.view.accessibility.AccessibilityEvent; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; @@ -29,18 +35,40 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; public class NotificationPanelView extends PanelView implements - ExpandableView.OnHeightChangedListener { + ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener, + View.OnClickListener { public static final boolean DEBUG_GESTURES = true; + private static final int EXPANSION_ANIMATION_LENGTH = 375; PhoneStatusBar mStatusBar; - private View mHeader; + private StatusBarHeaderView mHeader; + private QuickSettingsContainerView mQsContainer; private View mKeyguardStatusView; + private ObservableScrollView mScrollView; + private View mStackScrollerContainer; private NotificationStackScrollLayout mNotificationStackScroller; - private boolean mTrackingSettings; private int mNotificationTopPadding; private boolean mAnimateNextTopPaddingChange; + private Interpolator mExpansionInterpolator; + + private int mTrackingPointer; + private VelocityTracker mVelocityTracker; + private boolean mTracking; + private boolean mQsExpanded; + private float mInitialHeightOnTouch; + private float mInitialTouchX; + private float mInitialTouchY; + private float mQsExpansionHeight; + private int mQsMinExpansionHeight; + private int mQsMaxExpansionHeight; + private int mMinStackHeight; + private float mNotificationTranslation; + private int mStackScrollerIntrinsicPadding; + private boolean mQsExpansionEnabled = true; + private ValueAnimator mQsExpansionAnimator; + public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -63,14 +91,21 @@ public class NotificationPanelView extends PanelView implements @Override protected void onFinishInflate() { super.onFinishInflate(); - - mHeader = findViewById(R.id.header); + mHeader = (StatusBarHeaderView) findViewById(R.id.header); + mHeader.getBackgroundView().setOnClickListener(this); mKeyguardStatusView = findViewById(R.id.keyguard_status_view); + mStackScrollerContainer = findViewById(R.id.notification_container_parent); + mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container); + mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view); + mScrollView.setListener(this); mNotificationStackScroller = (NotificationStackScrollLayout) findViewById(R.id.notification_stack_scroller); mNotificationStackScroller.setOnHeightChangedListener(this); mNotificationTopPadding = getResources().getDimensionPixelSize( R.dimen.notifications_top_padding); + mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height); + mExpansionInterpolator = AnimationUtils.loadInterpolator( + getContext(), android.R.interpolator.fast_out_slow_in); } @Override @@ -78,11 +113,21 @@ public class NotificationPanelView extends PanelView implements super.onLayout(changed, left, top, right, bottom); int keyguardBottomMargin = ((MarginLayoutParams) mKeyguardStatusView.getLayoutParams()).bottomMargin; - mNotificationStackScroller.setTopPadding(mStatusBar.getBarState() == StatusBarState.KEYGUARD - ? mKeyguardStatusView.getBottom() + keyguardBottomMargin - : mHeader.getBottom() + mNotificationTopPadding, - mAnimateNextTopPaddingChange); - mAnimateNextTopPaddingChange = false; + if (!mQsExpanded) { + mStackScrollerIntrinsicPadding = mStatusBar.getBarState() == StatusBarState.KEYGUARD + ? mKeyguardStatusView.getBottom() + keyguardBottomMargin + : mHeader.getBottom() + mNotificationTopPadding; + mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding, + mAnimateNextTopPaddingChange); + mAnimateNextTopPaddingChange = false; + } + + // Calculate quick setting heights. + mQsMinExpansionHeight = mHeader.getCollapsedHeight(); + mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight(); + if (mQsExpansionHeight == 0) { + mQsExpansionHeight = mQsMinExpansionHeight; + } } public void animateNextTopPaddingChange() { @@ -90,6 +135,30 @@ public class NotificationPanelView extends PanelView implements requestLayout(); } + /** + * @return Whether Quick Settings are currently expanded. + */ + public boolean isQsExpanded() { + return mQsExpanded; + } + + public void setQsExpansionEnabled(boolean qsExpansionEnabled) { + mQsExpansionEnabled = qsExpansionEnabled; + mHeader.setExpansionEnabled(qsExpansionEnabled); + } + + public void closeQs() { + cancelAnimation(); + setQsExpansion(mQsMinExpansionHeight); + } + + public void openQs() { + cancelAnimation(); + if (mQsExpansionEnabled) { + setQsExpansion(mQsMaxExpansionHeight); + } + } + @Override public void fling(float vel, boolean always) { GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder(); @@ -114,42 +183,245 @@ public class NotificationPanelView extends PanelView implements @Override public boolean onInterceptTouchEvent(MotionEvent event) { - // intercept for quick settings - if (event.getAction() == MotionEvent.ACTION_DOWN) { - final View target = mStatusBar.getBarState() == StatusBarState.KEYGUARD - ? mKeyguardStatusView - : mHeader; - final boolean inTarget = PhoneStatusBar.inBounds(target, event, true); - if (inTarget && !isInSettings()) { - mTrackingSettings = true; - requestDisallowInterceptTouchEvent(true); - return true; - } - if (!inTarget && isInSettings()) { - mTrackingSettings = true; - requestDisallowInterceptTouchEvent(true); - return true; - } + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); } - return super.onInterceptTouchEvent(event); + final float x = event.getX(pointerIndex); + final float y = event.getY(pointerIndex); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mInitialTouchY = y; + mInitialTouchX = x; + initVelocityTracker(); + trackMovement(event); + if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) { + getParent().requestDisallowInterceptTouchEvent(true); + } + break; + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + mTrackingPointer = event.getPointerId(newIndex); + mInitialTouchX = event.getX(newIndex); + mInitialTouchY = event.getY(newIndex); + } + break; + + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY; + trackMovement(event); + if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) + && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) { + onQsExpansionStarted(); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = y; + mInitialTouchX = x; + mTracking = true; + return true; + } + break; + } + return !mQsExpanded && super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference // implementation. - if (mTrackingSettings) { - mStatusBar.onSettingsEvent(event); - if (event.getAction() == MotionEvent.ACTION_UP - || event.getAction() == MotionEvent.ACTION_CANCEL) { - mTrackingSettings = false; + if (mTracking) { + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float y = event.getY(pointerIndex); + final float x = event.getX(pointerIndex); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mTracking = true; + mInitialTouchY = y; + mInitialTouchX = x; + onQsExpansionStarted(); + mInitialHeightOnTouch = mQsExpansionHeight; + initVelocityTracker(); + trackMovement(event); + break; + + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + final float newY = event.getY(newIndex); + final float newX = event.getX(newIndex); + mTrackingPointer = event.getPointerId(newIndex); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = newY; + mInitialTouchX = newX; + } + break; + + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY; + setQsExpansion(h + mInitialHeightOnTouch); + trackMovement(event); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mTracking = false; + mTrackingPointer = -1; + trackMovement(event); + + float vel = getCurrentVelocity(); + + // TODO: Better logic whether we should expand or not. + flingSettings(vel, vel > 0); + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + break; } return true; } - if (isInSettings()) { - return true; + + // Consume touch events when QS are expanded. + return mQsExpanded || super.onTouchEvent(event); + } + + private void onQsExpansionStarted() { + cancelAnimation(); + + // Reset scroll position and apply that position to the expanded height. + float height = mQsExpansionHeight - mScrollView.getScrollY(); + mScrollView.scrollTo(0, 0); + setQsExpansion(height); + } + + private void expandQs() { + mHeader.setExpanded(true); + mNotificationStackScroller.setEnabled(false); + mScrollView.setVisibility(View.VISIBLE); + mQsExpanded = true; + } + + private void collapseQs() { + mHeader.setExpanded(false); + mNotificationStackScroller.setEnabled(true); + mScrollView.setVisibility(View.INVISIBLE); + mQsExpanded = false; + } + + private void setQsExpansion(float height) { + height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight); + if (height > mQsMinExpansionHeight && !mQsExpanded) { + expandQs(); + } else if (height <= mQsMinExpansionHeight && mQsExpanded) { + collapseQs(); + } + mQsExpansionHeight = height; + mHeader.setExpansion(height); + setQsTranslation(height); + setQsStackScrollerPadding(height); + } + + private void setQsTranslation(float height) { + mQsContainer.setY(height - mQsContainer.getHeight()); + } + + private void setQsStackScrollerPadding(float height) { + float start = height - mScrollView.getScrollY() + mNotificationTopPadding; + float stackHeight = mNotificationStackScroller.getHeight() - start; + if (stackHeight <= mMinStackHeight) { + float overflow = mMinStackHeight - stackHeight; + stackHeight = mMinStackHeight; + start = mNotificationStackScroller.getHeight() - stackHeight; + mNotificationStackScroller.setTranslationY(overflow); + mNotificationTranslation = overflow + mScrollView.getScrollY(); + } else { + mNotificationStackScroller.setTranslationY(0); + mNotificationTranslation = mScrollView.getScrollY(); + } + mNotificationStackScroller.setTopPadding(clampQsStackScrollerPadding((int) start), false); + } + + private int clampQsStackScrollerPadding(int desiredPadding) { + return Math.max(desiredPadding, mStackScrollerIntrinsicPadding); + } + + private void trackMovement(MotionEvent event) { + if (mVelocityTracker != null) mVelocityTracker.addMovement(event); + } + + private void initVelocityTracker() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + } + mVelocityTracker = VelocityTracker.obtain(); + } + + private float getCurrentVelocity() { + if (mVelocityTracker == null) { + return 0; + } + mVelocityTracker.computeCurrentVelocity(1000); + return mVelocityTracker.getYVelocity(); + } + + private void cancelAnimation() { + if (mQsExpansionAnimator != null) { + mQsExpansionAnimator.cancel(); + } + } + private void flingSettings(float vel, boolean expand) { + + // TODO: Actually use velocity. + + float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight; + ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); + animator.setDuration(EXPANSION_ANIMATION_LENGTH); + animator.setInterpolator(mExpansionInterpolator); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + setQsExpansion((Float) animation.getAnimatedValue()); + } + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mQsExpansionAnimator = null; + } + }); + animator.start(); + mQsExpansionAnimator = animator; + } + + /** + * @return Whether we should intercept a gesture to open Quick Settings. + */ + private boolean shouldIntercept(float x, float y, float yDiff) { + if (!mQsExpansionEnabled) { + return false; + } + View headerView = mStatusBar.getBarState() == StatusBarState.KEYGUARD && !mQsExpanded + ? mKeyguardStatusView + : mHeader; + boolean onHeader = x >= headerView.getLeft() && x <= headerView.getRight() + && y >= headerView.getTop() && y <= headerView.getBottom(); + if (mQsExpanded) { + return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0); + } else { + return onHeader; } - return super.onTouchEvent(event); } @Override @@ -164,14 +436,16 @@ public class NotificationPanelView extends PanelView implements protected int getMaxPanelHeight() { if (!isInSettings()) { int maxPanelHeight = super.getMaxPanelHeight(); - int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); + int notificationMarginBottom = mStackScrollerContainer.getPaddingBottom(); + int emptyBottomMargin = notificationMarginBottom + + mNotificationStackScroller.getEmptyBottomMargin(); return maxPanelHeight - emptyBottomMargin; } return super.getMaxPanelHeight(); } private boolean isInSettings() { - return mStatusBar != null && mStatusBar.isFlippedToSettings(); + return mQsExpanded; } @Override @@ -200,4 +474,24 @@ public class NotificationPanelView extends PanelView implements public void onHeightChanged(ExpandableView view) { requestPanelHeightUpdate(); } + + @Override + public void onScrollChanged() { + if (mQsExpanded) { + mNotificationStackScroller.setTranslationY( + mNotificationTranslation - mScrollView.getScrollY()); + } + } + + @Override + public void onClick(View v) { + if (v == mHeader.getBackgroundView()) { + onQsExpansionStarted(); + if (mQsExpanded) { + flingSettings(0 /* vel */, false /* expand */); + } else if (mQsExpansionEnabled) { + flingSettings(0 /* vel */, true /* expand */); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java new file mode 100644 index 0000000..f41e78d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java @@ -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 + */ + +package com.android.systemui.statusbar.phone; + +import android.content.Context; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * The container with notification stack scroller and quick settings inside. + */ +public class NotificationsQuickSettingsContainer extends FrameLayout { + + public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected boolean fitSystemWindows(Rect insets) { + setPadding(0, 0, 0, insets.bottom); + insets.bottom = 0; + return true; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java new file mode 100644 index 0000000..46484f3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.phone; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ScrollView; + +/** + * A scroll view which can be observed for scroll change events. + */ +public class ObservableScrollView extends ScrollView { + + private Listener mListener; + + public ObservableScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setListener(Listener listener) { + mListener = listener; + } + + public boolean isScrolledToBottom() { + return getScrollY() == getMaxScrollY(); + } + + private int getMaxScrollY() { + int scrollRange = 0; + if (getChildCount() > 0) { + View child = getChildAt(0); + scrollRange = Math.max(0, + child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop)); + } + return scrollRange; + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + if (mListener != null) { + mListener.onScrollChanged(); + } + } + + public interface Listener { + void onScrollChanged(); + } +} 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 0cdca66..8c70517 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -75,7 +75,7 @@ public class PanelView extends FrameLayout { private boolean mClosing; private boolean mTracking; private int mTrackingPointer; - private int mTouchSlop; + protected int mTouchSlop; private TimeAnimator mTimeAnimator; private ObjectAnimator mPeekAnimator; @@ -220,9 +220,9 @@ public class PanelView extends FrameLayout { private float mVel, mAccel; protected int mMaxPanelHeight = 0; private String mViewName; - protected float mInitialTouchY; - protected float mInitialTouchX; - protected float mFinalTouchY; + private float mInitialTouchY; + private float mInitialTouchX; + private float mFinalTouchY; protected void onExpandingFinished() { } @@ -526,11 +526,14 @@ public class PanelView extends FrameLayout { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: + if (mTimeAnimator.isRunning()) { + mTimeAnimator.cancel(); // end any outstanding animations + return true; + } mInitialTouchY = y; mInitialTouchX = x; initVelocityTracker(); trackMovement(event); - mTimeAnimator.cancel(); // end any outstanding animations break; case MotionEvent.ACTION_POINTER_UP: final int upPointer = event.getPointerId(event.getActionIndex()); @@ -569,7 +572,7 @@ public class PanelView extends FrameLayout { } protected boolean isScrolledToBottom() { - return false; + return true; } protected float getContentHeight() { 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 f945c79..92eee4e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -73,7 +73,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewPropertyAnimator; @@ -192,8 +191,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, int mIconHPadding = -1; Display mDisplay; Point mCurrentDisplaySize = new Point(); - private float mHeadsUpVerticalOffset; - private int[] mStackScrollerPosition = new int[2]; StatusBarWindowView mStatusBarWindow; PhoneStatusBarView mStatusBarView; @@ -222,14 +219,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window View mExpandedContents; int mNotificationPanelGravity; - int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx; + int mNotificationPanelMarginBottomPx; float mNotificationPanelMinHeightFrac; boolean mNotificationPanelIsFullScreenWidth; TextView mNotificationPanelDebugText; // settings QuickSettings mQS; - boolean mHasQuickSettings; View mFlipSettingsView; QuickSettingsContainerView mSettingsContainer; @@ -245,14 +241,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, int mKeyguardMaxNotificationCount; View mDateTimeView; View mClearButton; - FlipperButton mHeaderFlipper, mKeyguardFlipper; + ImageView mHeaderFlipper; // carrier/wifi label private TextView mCarrierLabel; private boolean mCarrierLabelVisible = false; private int mCarrierLabelHeight; private TextView mEmergencyCallLabel; - private int mNotificationHeaderHeight; + private int mStatusBarHeaderHeight; private View mKeyguardCarrierLabel; private boolean mShowCarrierInPanel = false; @@ -326,11 +322,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " + "selfChange=%s userSetup=%s mUserSetup=%s", selfChange, userSetup, mUserSetup)); - mHeaderFlipper.userSetup(userSetup); - mKeyguardFlipper.userSetup(userSetup); if (userSetup != mUserSetup) { mUserSetup = userSetup; + if (mNotificationPanel != null) { + mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && userSetup); + } if (!mUserSetup && mStatusBarView != null) animateCollapseQuickSettings(); } @@ -631,8 +628,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); mKeyguardIconOverflowContainer.setOnActivatedListener(this); mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text); - // TODO: Comment in when transition is ready. - //mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); + mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); mStackScroller.addView(mKeyguardIconOverflowContainer); mExpandedContents = mStackScroller; @@ -651,16 +647,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mClearButton.setEnabled(false); mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date); - mHasQuickSettings = res.getBoolean(R.bool.config_hasQuickSettings); - mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime); if (mDateTimeView != null) { mDateTimeView.setOnClickListener(mClockClickListener); mDateTimeView.setEnabled(true); } - mHeaderFlipper = new FlipperButton(mStatusBarWindow.findViewById(R.id.header_flipper)); - mKeyguardFlipper =new FlipperButton(mStatusBarWindow.findViewById(R.id.keyguard_flipper)); + mHeaderFlipper = (ImageView) mStatusBarWindow.findViewById(R.id.header_flipper); if (!mNotificationPanelIsFullScreenWidth) { mNotificationPanel.setSystemUiVisibility( @@ -735,26 +728,23 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // updateCarrierLabelVisibility(false); } - // Quick Settings (where available, some restrictions apply) - if (mHasQuickSettings) { - // Quick Settings needs a container to survive - mSettingsContainer = (QuickSettingsContainerView) - mStatusBarWindow.findViewById(R.id.quick_settings_container); - mFlipSettingsView = mSettingsContainer; - if (mSettingsContainer != null) { - mQS = new QuickSettings(mContext, mSettingsContainer); - if (!mNotificationPanelIsFullScreenWidth) { - mSettingsContainer.setSystemUiVisibility( - View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS - | View.STATUS_BAR_DISABLE_SYSTEM_INFO); - } - mQS.setService(this); - mQS.setBar(mStatusBarView); - mQS.setup(mNetworkController, mBluetoothController, mBatteryController, - mLocationController, mRotationLockController); - } else { - mQS = null; // fly away, be free - } + // Quick Settings needs a container to survive + mSettingsContainer = (QuickSettingsContainerView) + mStatusBarWindow.findViewById(R.id.quick_settings_container); + mFlipSettingsView = mSettingsContainer; + if (mSettingsContainer != null) { + mQS = new QuickSettings(mContext, mSettingsContainer); + if (!mNotificationPanelIsFullScreenWidth) { + mSettingsContainer.setSystemUiVisibility( + View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS + | View.STATUS_BAR_DISABLE_SYSTEM_INFO); + } + mQS.setService(this); + mQS.setBar(mStatusBarView); + mQS.setup(mNetworkController, mBluetoothController, mBatteryController, + mLocationController, mRotationLockController); + } else { + mQS = null; // fly away, be free } PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -775,101 +765,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mStatusBarView; } - public boolean onSettingsEvent(MotionEvent event) { - userActivity(); - if (mSettingsClosing - && mFlipSettingsViewAnim != null && mFlipSettingsViewAnim.isRunning()) { - return true; - } - if (mSettingsTracker != null) { - mSettingsTracker.addMovement(event); - } - final int slop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - mSettingsTracker = VelocityTracker.obtain(); - mSettingsDownY = event.getY(); - mSettingsCancelled = false; - mSettingsStarted = false; - mSettingsClosing = mFlipSettingsView.getVisibility() == View.VISIBLE; - if (mSettingsClosing) { - mStackScroller.setVisibility(View.VISIBLE); - } else { - mFlipSettingsView.setTranslationY(-mNotificationPanel.getMeasuredHeight()); - } - dispatchSettingsEvent(event); - } else if (mSettingsTracker != null && (event.getAction() == MotionEvent.ACTION_UP - || event.getAction() == MotionEvent.ACTION_CANCEL)) { - final float dy = event.getY() - mSettingsDownY; - final FlipperButton flipper = mState == StatusBarState.KEYGUARD - ? mKeyguardFlipper - : mHeaderFlipper; - final boolean inButton = flipper.inHolderBounds(event); - final boolean qsTap = mSettingsClosing && Math.abs(dy) < slop; - if (!qsTap && !inButton) { - mSettingsTracker.computeCurrentVelocity(1000); - final float vy = mSettingsTracker.getYVelocity(); - final boolean animate = true; - if (dy <= slop || vy <= 0) { - flipToNotifications(animate); - } else { - flipToSettings(animate); - } - } - mSettingsTracker.recycle(); - mSettingsTracker = null; - dispatchSettingsEvent(event); - } else if (mSettingsTracker != null && event.getAction() == MotionEvent.ACTION_MOVE) { - final float dy = event.getY() - mSettingsDownY; - if (mSettingsClosing) { - positionSettings(dy); - final boolean qsTap = Math.abs(dy) < slop; - if (!mSettingsCancelled && !qsTap) { - MotionEvent cancelEvent = MotionEvent.obtainNoHistory(event); - cancelEvent.setAction(MotionEvent.ACTION_CANCEL); - dispatchSettingsEvent(cancelEvent); - mSettingsCancelled = true; - } - } else { - if (!mSettingsStarted && dy > slop) { - mSettingsStarted = true; - mFlipSettingsView.setVisibility(View.VISIBLE); - mStackScroller.setVisibility(View.VISIBLE); - } - if (mSettingsStarted) { - positionSettings(dy); - } - dispatchSettingsEvent(event); - } - } - return true; - } - - private void dispatchSettingsEvent(MotionEvent event) { - final View target = mSettingsClosing ? mFlipSettingsView : mNotificationPanelHeader; - final int[] targetLoc = new int[2]; - target.getLocationInWindow(targetLoc); - final int[] panelLoc = new int[2]; - mNotificationPanel.getLocationInWindow(panelLoc); - final int dx = targetLoc[0] - panelLoc[0]; - final int dy = targetLoc[1] - panelLoc[1]; - event.offsetLocation(-dx, -dy); - target.dispatchTouchEvent(event); - } - - private void positionSettings(float dy) { - if (mSettingsClosing) { - final int ph = mNotificationPanel.getMeasuredHeight(); - dy = Math.min(Math.max(-ph, dy), 0); - mFlipSettingsView.setTranslationY(dy); - mStackScroller.setTranslationY(ph + dy); - } else { - final int h = mFlipSettingsView.getBottom(); - dy = Math.min(Math.max(0, dy), h); - mFlipSettingsView.setTranslationY(-h + dy); - mStackScroller.setTranslationY(dy); - } - } - private void startKeyguard() { KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class); mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, @@ -1230,8 +1125,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear); } - mHeaderFlipper.refreshLayout(); - mKeyguardFlipper.refreshLayout(); + if (mHeaderFlipper != null) { + // Force asset reloading + mHeaderFlipper.setImageDrawable(null); + mHeaderFlipper.setImageResource(R.drawable.ic_notify_quicksettings); + } refreshAllStatusBarIcons(); } @@ -1286,8 +1184,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - mHeaderFlipper.provisionCheck(provisioned); - mKeyguardFlipper.provisionCheck(provisioned); + mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup); } @Override @@ -1362,7 +1259,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final boolean makeVisible = !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) && mStackScroller.getHeight() < (mNotificationPanel.getHeight() - - mCarrierLabelHeight - mNotificationHeaderHeight) + - mCarrierLabelHeight - mStatusBarHeaderHeight) && mStackScroller.getVisibility() == View.VISIBLE && mState != StatusBarState.KEYGUARD; @@ -1775,47 +1672,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } public void flipToNotifications(boolean animate) { - cancelAnim(mFlipSettingsViewAnim); - cancelAnim(mScrollViewAnim); - cancelAnim(mClearButtonAnim); - mHeaderFlipper.cancel(); - mKeyguardFlipper.cancel(); - mStackScroller.setVisibility(View.VISIBLE); - final int h = mNotificationPanel.getMeasuredHeight(); - if (animate) { - final float settingsY = - mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : 0; - final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : h; - mScrollViewAnim = start( - interpolator(mDecelerateInterpolator, - ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, 0) - .setDuration(FLIP_DURATION) - )); - mFlipSettingsViewAnim = start( - setVisibilityWhenDone( - interpolator(mDecelerateInterpolator, - ObjectAnimator.ofFloat( - mFlipSettingsView, View.TRANSLATION_Y, settingsY, -h)) - .setDuration(FLIP_DURATION), - mFlipSettingsView, View.INVISIBLE)); - } else { - mStackScroller.setTranslationY(0); - mFlipSettingsView.setTranslationY(-h); - mFlipSettingsView.setVisibility(View.INVISIBLE); - } - mHeaderFlipper.flipToNotifications(animate); - mKeyguardFlipper.flipToNotifications(animate); - mClearButton.setVisibility(View.VISIBLE); - mClearButton.setAlpha(0f); - setAreThereNotifications(); // this will show/hide the button as necessary - mNotificationPanel.postDelayed(new Runnable() { - public void run() { - updateCarrierLabelVisibility(false); - } - }, animate ? FLIP_DURATION - 150 : 0); - if (mOnFlipRunnable != null) { - mOnFlipRunnable.run(); - } + // TODO: Animation + mNotificationPanel.closeQs(); } @Override @@ -1829,78 +1687,18 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (!mUserSetup) return; mNotificationPanel.expand(); - if (mFlipSettingsView.getVisibility() != View.VISIBLE - || mFlipSettingsView.getTranslationY() < 0) { - flipToSettings(true /*animate*/); - } + mNotificationPanel.openQs(); if (false) postStartTracing(); } public boolean isFlippedToSettings() { - if (mFlipSettingsView != null) { - return mFlipSettingsView.getVisibility() == View.VISIBLE; + if (mNotificationPanel != null) { + return mNotificationPanel.isQsExpanded(); } return false; } - public void flipToSettings(boolean animate) { - // Settings are not available in setup - if (!mUserSetup) return; - - cancelAnim(mFlipSettingsViewAnim); - cancelAnim(mScrollViewAnim); - mHeaderFlipper.cancel(); - mKeyguardFlipper.cancel(); - cancelAnim(mClearButtonAnim); - - mFlipSettingsView.setVisibility(View.VISIBLE); - final int h = mNotificationPanel.getMeasuredHeight(); - if (animate) { - final float settingsY - = mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : -h; - final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : 0; - mFlipSettingsViewAnim = start( - startDelay(0, - interpolator(mDecelerateInterpolator, - ObjectAnimator.ofFloat(mFlipSettingsView, View.TRANSLATION_Y, - settingsY, 0f) - .setDuration(FLIP_DURATION) - ))); - mScrollViewAnim = start( - setVisibilityWhenDone( - interpolator(mDecelerateInterpolator, - ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, h) - ) - .setDuration(FLIP_DURATION), - mStackScroller, View.INVISIBLE)); - } else { - mFlipSettingsView.setTranslationY(0); - mStackScroller.setTranslationY(h); - mStackScroller.setVisibility(View.INVISIBLE); - } - mHeaderFlipper.flipToSettings(animate); - mKeyguardFlipper.flipToSettings(animate); - if (animate) { - mClearButtonAnim = start( - setVisibilityWhenDone( - ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f) - .setDuration(FLIP_DURATION), - mClearButton, View.INVISIBLE)); - } else { - mClearButton.setAlpha(0); - mClearButton.setVisibility(View.INVISIBLE); - } - mNotificationPanel.postDelayed(new Runnable() { - public void run() { - updateCarrierLabelVisibility(false); - } - }, animate ? FLIP_DURATION - 150 : 0); - if (mOnFlipRunnable != null) { - mOnFlipRunnable.run(); - } - } - public void animateCollapseQuickSettings() { mStatusBarView.collapseAllPanels(true); } @@ -1927,12 +1725,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mStackScroller.setVisibility(View.VISIBLE); mNotificationPanel.setVisibility(View.GONE); - mFlipSettingsView.setVisibility(View.GONE); setAreThereNotifications(); // show the clear button - mHeaderFlipper.reset(); - mKeyguardFlipper.reset(); + mNotificationPanel.closeQs(); mExpandedVisible = false; if (mNavigationBarView != null) @@ -2235,7 +2031,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { + public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; int flags = mNavigationIconHints; if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { @@ -2243,7 +2040,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } else { flags &= ~NAVIGATION_HINT_BACK_ALT; } - if (imeShown) { + if (showImeSwitcher) { flags |= NAVIGATION_HINT_IME_SHOWN; } else { flags &= ~NAVIGATION_HINT_IME_SHOWN; @@ -2462,20 +2259,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - void updateExpandedInvisiblePosition() { - mTrackingPosition = -mDisplayMetrics.heightPixels; - } - static final float saturate(float a) { return a < 0f ? 0f : (a > 1f ? 1f : a); } @Override - protected int getExpandedViewMaxHeight() { - return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx; - } - - @Override public void updateExpandedViewPos(int thingy) { if (SPEW) Log.v(TAG, "updateExpandedViewPos"); @@ -2485,15 +2273,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams(); lp.gravity = mNotificationPanelGravity; - lp.setMarginStart(mNotificationPanelMarginPx); mNotificationPanel.setLayoutParams(lp); - if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) { - mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx); - mStackScroller.getLocationOnScreen(mStackScrollerPosition); - mHeadsUpVerticalOffset = mStackScrollerPosition[1] - mNaturalBarHeight; - } - updateCarrierLabelVisibility(false); } @@ -2541,17 +2322,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, animateCollapsePanels(); } - private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() { - public void onClick(View v) { - if (mHasQuickSettings) { - animateExpandSettingsPanel(); - } else { - startActivityDismissingKeyguard( - new Intent(android.provider.Settings.ACTION_SETTINGS), true); - } - } - }; - private View.OnClickListener mClockClickListener = new View.OnClickListener() { public void onClick(View v) { startActivityDismissingKeyguard( @@ -2652,17 +2422,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } - public void animateHeadsUp(boolean animateInto, float frac) { - if (!ENABLE_HEADS_UP || mHeadsUpNotificationView == null) return; - frac = frac / 0.4f; - frac = frac < 1.0f ? frac : 1.0f; - float alpha = 1.0f - frac; - float offset = mHeadsUpVerticalOffset * frac; - offset = animateInto ? offset : 0f; - mHeadsUpNotificationView.setAlpha(alpha); - mHeadsUpNotificationView.setY(offset); - } - public void onHeadsUpDismissed() { if (mInterruptingNotificationEntry == null) return; mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); @@ -2735,17 +2494,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity); - mNotificationPanelMarginBottomPx - = (int) res.getDimension(R.dimen.notification_panel_margin_bottom); - mNotificationPanelMarginPx - = (int) res.getDimension(R.dimen.notification_panel_margin_left); mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity); if (mNotificationPanelGravity <= 0) { mNotificationPanelGravity = Gravity.START | Gravity.TOP; } mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height); - mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height); + mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height); mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1); if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) { @@ -3015,9 +2770,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void updateKeyguardState() { if (mState == StatusBarState.KEYGUARD) { - if (isFlippedToSettings()) { - flipToNotifications(false /*animate*/); - } mKeyguardStatusView.setVisibility(View.VISIBLE); mKeyguardBottomArea.setVisibility(View.VISIBLE); mKeyguardIndicationTextView.setVisibility(View.VISIBLE); @@ -3025,7 +2777,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardCarrierLabel.setVisibility(View.VISIBLE); mNotificationPanelHeader.setVisibility(View.GONE); - mKeyguardFlipper.setVisibility(View.VISIBLE); + mNotificationPanel.closeQs(); mSettingsContainer.setKeyguardShowing(true); } else { mKeyguardStatusView.setVisibility(View.GONE); @@ -3034,10 +2786,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardCarrierLabel.setVisibility(View.GONE); mNotificationPanelHeader.setVisibility(View.VISIBLE); - mKeyguardFlipper.setVisibility(View.GONE); mSettingsContainer.setKeyguardShowing(false); } + updateStackScrollerState(); updatePublicMode(); updateRowStates(); checkBarModes(); @@ -3045,6 +2797,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, updateCarrierLabelVisibility(false); } + public void updateStackScrollerState() { + mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */); + } + public void userActivity() { if (mState == StatusBarState.KEYGUARD) { mKeyguardViewMediatorCallback.userActivity(); @@ -3098,7 +2854,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void onActivated(View view) { userActivity(); mKeyguardIndicationTextView.switchIndication(R.string.notification_tap_again); - super.onActivated(view); + mStackScroller.setActivatedChild(view); } /** @@ -3110,9 +2866,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void onReset(View view) { - super.onReset(view); - mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase); + public void onActivationReset(View view) { + if (view == mStackScroller.getActivatedChild()) { + mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase); + mStackScroller.setActivatedChild(null); + } } public void onTrackingStarted() { @@ -3144,30 +2902,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void onReset() { - int n = mNotificationData.size(); - for (int i = 0; i < n; i++) { - NotificationData.Entry entry = mNotificationData.get(i); - if (entry.row.getVisibility() != View.GONE) { - entry.row.setDimmed(true /* dimmed */, true /* fade */); - } - } - if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) { - mKeyguardIconOverflowContainer.setDimmed(true /* dimmed */, true /* fade */); - } + public void onDragDownReset() { + mStackScroller.setDimmed(true /* dimmed */, true /* animated */); } public void onThresholdReached() { - int n = mNotificationData.size(); - for (int i = 0; i < n; i++) { - NotificationData.Entry entry = mNotificationData.get(i); - if (entry.row.getVisibility() != View.GONE) { - entry.row.setDimmed(false /* dimmed */, true /* fade */); - } - } - if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) { - mKeyguardIconOverflowContainer.setDimmed(false /* dimmed */, true /* fade */); - } + mStackScroller.setDimmed(false /* dimmed */, true /* animate */); } /** @@ -3201,131 +2941,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, * @return a ViewGroup that spans the entire panel which contains the quick settings */ public ViewGroup getQuickSettingsOverlayParent() { - if (mHasQuickSettings) { - return mNotificationPanel; - } else { - return null; - } - } - - public static boolean inBounds(View view, MotionEvent event, boolean orAbove) { - final int[] location = new int[2]; - view.getLocationInWindow(location); - final int rx = (int) event.getRawX(); - final int ry = (int) event.getRawY(); - return rx >= location[0] && rx <= location[0] + view.getMeasuredWidth() - && (orAbove || ry >= location[1]) && ry <= location[1] + view.getMeasuredHeight(); - } - - private final class FlipperButton { - private final View mHolder; - - private ImageView mSettingsButton, mNotificationButton; - private Animator mSettingsButtonAnim, mNotificationButtonAnim; - - public FlipperButton(View holder) { - mHolder = holder; - mSettingsButton = (ImageView) holder.findViewById(R.id.settings_button); - if (mSettingsButton != null) { - mSettingsButton.setOnClickListener(mSettingsButtonListener); - if (mHasQuickSettings) { - // the settings panel is hiding behind this button - mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings); - mSettingsButton.setVisibility(View.VISIBLE); - } else { - // no settings panel, go straight to settings - mSettingsButton.setVisibility(View.VISIBLE); - mSettingsButton.setImageResource(R.drawable.ic_notify_settings); - } - } - mNotificationButton = (ImageView) holder.findViewById(R.id.notification_button); - if (mNotificationButton != null) { - mNotificationButton.setOnClickListener(mNotificationButtonListener); - } - } - - public boolean inHolderBounds(MotionEvent event) { - return inBounds(mHolder, event, false); - } - - public void provisionCheck(boolean provisioned) { - if (mSettingsButton != null) { - mSettingsButton.setEnabled(provisioned); - } - } - - public void userSetup(boolean userSetup) { - if (mSettingsButton != null) { - mSettingsButton.setVisibility(userSetup ? View.VISIBLE : View.INVISIBLE); - } - } - - public void reset() { - cancel(); - mSettingsButton.setVisibility(View.VISIBLE); - mNotificationButton.setVisibility(View.GONE); - } - - public void refreshLayout() { - if (mSettingsButton != null) { - // Force asset reloading - mSettingsButton.setImageDrawable(null); - mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings); - } - - if (mNotificationButton != null) { - // Force asset reloading - mNotificationButton.setImageDrawable(null); - mNotificationButton.setImageResource(R.drawable.ic_notifications); - } - } - - public void flipToSettings(boolean animate) { - mNotificationButton.setVisibility(View.VISIBLE); - if (animate) { - mSettingsButtonAnim = start( - setVisibilityWhenDone( - ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f) - .setDuration(FLIP_DURATION_OUT), - mStackScroller, View.INVISIBLE)); - mNotificationButtonAnim = start( - startDelay(FLIP_DURATION_OUT, - ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f) - .setDuration(FLIP_DURATION_IN))); - } else { - mSettingsButton.setAlpha(0f); - mSettingsButton.setVisibility(View.INVISIBLE); - mNotificationButton.setAlpha(1f); - } - } - - public void flipToNotifications(boolean animate) { - mSettingsButton.setVisibility(View.VISIBLE); - if (animate) { - mNotificationButtonAnim = start( - setVisibilityWhenDone( - ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f) - .setDuration(FLIP_DURATION_OUT), - mNotificationButton, View.INVISIBLE)); - - mSettingsButtonAnim = start( - startDelay(FLIP_DURATION_OUT, - ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f) - .setDuration(FLIP_DURATION_IN))); - } else { - mNotificationButton.setVisibility(View.INVISIBLE); - mNotificationButton.setAlpha(0f); - mSettingsButton.setAlpha(1f); - } - } - - public void cancel() { - cancelAnim(mSettingsButtonAnim); - cancelAnim(mNotificationButtonAnim); - } - - public void setVisibility(int vis) { - mHolder.setVisibility(vis); - } + return mNotificationPanel; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 10c1625..e6de057 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -220,8 +220,6 @@ public class PhoneStatusBarView extends PanelBar { panel.setAlpha(alpha); } - mBar.animateHeadsUp(mNotificationPanel == panel, mPanelExpandedFractionSum); - mBar.updateCarrierLabelVisibility(false); mBar.userActivity(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java new file mode 100644 index 0000000..9d33930 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.phone; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import com.android.systemui.R; + +/** + * The view to manage the header area in the expanded status bar. + */ +public class StatusBarHeaderView extends RelativeLayout { + + private boolean mExpanded; + private View mBackground; + private View mFlipper; + + private int mCollapsedHeight; + private int mExpandedHeight; + + public StatusBarHeaderView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mBackground = findViewById(R.id.background); + mFlipper = findViewById(R.id.header_flipper); + loadDimens(); + } + + private void loadDimens() { + mCollapsedHeight = getResources().getDimensionPixelSize( + R.dimen.status_bar_header_height); + mExpandedHeight = getResources().getDimensionPixelSize( + R.dimen.status_bar_header_height_expanded); + } + + public int getCollapsedHeight() { + return mCollapsedHeight; + } + + public int getExpandedHeight() { + return mExpandedHeight; + } + + public void setExpanded(boolean expanded) { + if (expanded != mExpanded) { + ViewGroup.LayoutParams lp = getLayoutParams(); + lp.height = expanded ? mExpandedHeight : mCollapsedHeight; + setLayoutParams(lp); + mExpanded = expanded; + } + } + + public void setExpansionEnabled(boolean enabled) { + mFlipper.setVisibility(enabled ? View.VISIBLE : View.GONE); + } + + public void setExpansion(float height) { + if (height < mCollapsedHeight) { + height = mCollapsedHeight; + } + if (height > mExpandedHeight) { + height = mExpandedHeight; + } + if (mExpanded) { + mBackground.setTranslationY(-(mExpandedHeight - height)); + } else { + mBackground.setTranslationY(0); + } + } + + public View getBackgroundView() { + return mBackground; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index e24ddd9..f24c1b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -95,7 +95,9 @@ public class StatusBarKeyguardViewManager { } private void showBouncer() { - mBouncer.show(); + if (!mOccluded) { + mBouncer.show(); + } updateStates(); } @@ -103,7 +105,12 @@ public class StatusBarKeyguardViewManager { * Reset the state of the view. */ public void reset() { - showBouncerOrKeyguard(); + if (mOccluded) { + mPhoneStatusBar.hideKeyguard(); + mBouncer.hide(); + } else { + showBouncerOrKeyguard(); + } updateStates(); } @@ -114,6 +121,7 @@ public class StatusBarKeyguardViewManager { public void onScreenTurnedOn(final IKeyguardShowCallback callback) { mScreenOn = true; + reset(); if (callback != null) { callbackAfterDraw(callback); } @@ -147,7 +155,7 @@ public class StatusBarKeyguardViewManager { public void setOccluded(boolean occluded) { mOccluded = occluded; mStatusBarWindowManager.setKeyguardOccluded(occluded); - updateStates(); + reset(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 4c9264d..e802d18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -56,11 +56,14 @@ public class StatusBarWindowView extends FrameLayout { @Override protected boolean fitSystemWindows(Rect insets) { if (getFitsSystemWindows()) { - setPadding(insets.left, insets.top, insets.right, insets.bottom); + setPadding(insets.left, insets.top, insets.right, 0); + insets.left = 0; + insets.top = 0; + insets.right = 0; } else { setPadding(0, 0, 0, 0); } - return true; + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java index 72e22e9..81e2cb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java @@ -237,6 +237,10 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. } @Override + public void onChildSnappedBack(View animView) { + } + + @Override public View getChildAtPosition(MotionEvent ev) { return mContentHolder; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java new file mode 100644 index 0000000..4121a40 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.stack; + +import android.view.View; + +import java.util.ArrayList; + +/** + * A global state to track all input states for the algorithm. + */ +public class AmbientState { + private ArrayList<View> mDraggedViews = new ArrayList<View>(); + private int mScrollY; + private boolean mDimmed; + private View mActivatedChild; + + public int getScrollY() { + return mScrollY; + } + + public void setScrollY(int scrollY) { + this.mScrollY = scrollY; + } + + public void onBeginDrag(View view) { + mDraggedViews.add(view); + } + + public void onDragFinished(View view) { + mDraggedViews.remove(view); + } + + public ArrayList<View> getDraggedViews() { + return mDraggedViews; + } + + /** + * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are + * translucent and everything is scaled back a bit. + */ + public void setDimmed(boolean dimmed) { + mDimmed = dimmed; + } + + /** + * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap + * interaction. This child is then scaled normally and its background is fully opaque. + */ + public void setActivatedChild(View activatedChild) { + mActivatedChild = activatedChild; + } + + public boolean isDimmed() { + return mDimmed; + } + + public View getActivatedChild() { + return mActivatedChild; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java new file mode 100644 index 0000000..41914ed --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.stack; + +import java.util.ArrayList; + +/** + * Filters the animations for only a certain type of properties. + */ +public class AnimationFilter { + boolean animateAlpha; + boolean animateY; + boolean animateZ; + boolean animateScale; + boolean animateHeight; + boolean animateDimmed; + + public AnimationFilter animateAlpha() { + animateAlpha = true; + return this; + } + + public AnimationFilter animateY() { + animateY = true; + return this; + } + + public AnimationFilter animateZ() { + animateZ = true; + return this; + } + + public AnimationFilter animateScale() { + animateScale = true; + return this; + } + + public AnimationFilter animateHeight() { + animateHeight = true; + return this; + } + + public AnimationFilter animateDimmed() { + animateDimmed = true; + return this; + } + + /** + * Combines multiple filters into {@code this} filter, using or as the operand . + * + * @param events The animation events from the filters to combine. + */ + public void applyCombination(ArrayList<NotificationStackScrollLayout.AnimationEvent> events) { + reset(); + int size = events.size(); + for (int i = 0; i < size; i++) { + combineFilter(events.get(i).filter); + } + } + + private void combineFilter(AnimationFilter filter) { + animateAlpha |= filter.animateAlpha; + animateY |= filter.animateY; + animateZ |= filter.animateZ; + animateScale |= filter.animateScale; + animateHeight |= filter.animateHeight; + animateDimmed |= filter.animateDimmed; + } + + private void reset() { + animateAlpha = false; + animateY = false; + animateZ = false; + animateScale = false; + animateHeight = false; + animateDimmed = false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index e4e5fb1..afd5068 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -84,7 +84,6 @@ public class NotificationStackScrollLayout extends ViewGroup private int mEmptyMarginBottom; private int mPaddingBetweenElements; private int mTopPadding; - private boolean mListenForHeightChanges = true; /** * The algorithm which calculates the properties for our children @@ -95,8 +94,11 @@ public class NotificationStackScrollLayout extends ViewGroup * The current State this Layout is in */ private StackScrollState mCurrentStackScrollState = new StackScrollState(this); + private AmbientState mAmbientState = new AmbientState(); private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>(); private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>(); + private ArrayList<View> mSnappedBackChildren = new ArrayList<View>(); + private ArrayList<View> mDragAnimPendingChildren = new ArrayList<View>(); private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<AnimationEvent>(); private ArrayList<View> mSwipedOutViews = new ArrayList<View>(); @@ -106,6 +108,8 @@ public class NotificationStackScrollLayout extends ViewGroup private ExpandableView.OnHeightChangedListener mOnHeightChangedListener; private boolean mNeedsAnimation; private boolean mTopPaddingNeedsAnimation; + private boolean mDimmedNeedsAnimation; + private boolean mActivateNeedsAnimation; private boolean mIsExpanded = true; private boolean mChildrenUpdateRequested; private ViewTreeObserver.OnPreDrawListener mChildrenUpdater @@ -265,8 +269,8 @@ public class NotificationStackScrollLayout extends ViewGroup * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout. */ private void updateChildren() { - mCurrentStackScrollState.setScrollY(mOwnScrollY); - mStackScrollAlgorithm.getStackScrollState(mCurrentStackScrollState); + mAmbientState.setScrollY(mOwnScrollY); + mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState); if (!isCurrentlyAnimating() && !mNeedsAnimation) { applyCurrentState(); } else { @@ -377,11 +381,34 @@ public class NotificationStackScrollLayout extends ViewGroup veto.performClick(); } setSwipingInProgress(false); + if (mDragAnimPendingChildren.contains(v)) { + // We start the swipe and finish it in the same frame, we don't want any animation + // for the drag + mDragAnimPendingChildren.remove(v); + } mSwipedOutViews.add(v); + mAmbientState.onDragFinished(v); + } + + @Override + public void onChildSnappedBack(View animView) { + mAmbientState.onDragFinished(animView); + if (!mDragAnimPendingChildren.contains(animView)) { + mSnappedBackChildren.add(animView); + requestChildrenUpdate(); + mNeedsAnimation = true; + } else { + // We start the swipe and snap back in the same frame, we don't want any animation + mDragAnimPendingChildren.remove(animView); + } } public void onBeginDrag(View v) { setSwipingInProgress(true); + mDragAnimPendingChildren.add(v); + mAmbientState.onBeginDrag(v); + requestChildrenUpdate(); + mNeedsAnimation = true; } public void onDragCancelled(View v) { @@ -467,6 +494,9 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean onTouchEvent(MotionEvent ev) { + if (!isEnabled()) { + return false; + } boolean scrollerWantsIt = false; if (!mSwipingInProgress) { scrollerWantsIt = onScrollTouch(ev); @@ -486,7 +516,7 @@ public class NotificationStackScrollLayout extends ViewGroup switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: { - if (getChildCount() == 0) { + if (getChildCount() == 0 || !isInContentBounds(ev)) { return false; } boolean isBeingDragged = !mScroller.isFinished(); @@ -934,12 +964,32 @@ public class NotificationStackScrollLayout extends ViewGroup private void generateChildHierarchyEvents() { generateChildAdditionEvents(); generateChildRemovalEvents(); + generateSnapBackEvents(); + generateDragEvents(); generateTopPaddingEvent(); + generateActivateEvent(); + generateDimmedEvent(); mNeedsAnimation = false; } + private void generateSnapBackEvents() { + for (View child : mSnappedBackChildren) { + mAnimationEvents.add(new AnimationEvent(child, + AnimationEvent.ANIMATION_TYPE_SNAP_BACK)); + } + mSnappedBackChildren.clear(); + } + + private void generateDragEvents() { + for (View child : mDragAnimPendingChildren) { + mAnimationEvents.add(new AnimationEvent(child, + AnimationEvent.ANIMATION_TYPE_START_DRAG)); + } + mDragAnimPendingChildren.clear(); + } + private void generateChildRemovalEvents() { - for (View child : mChildrenToRemoveAnimated) { + for (View child : mChildrenToRemoveAnimated) { boolean childWasSwipedOut = mSwipedOutViews.contains(child); int animationType = childWasSwipedOut ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT @@ -951,7 +1001,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private void generateChildAdditionEvents() { - for (View child : mChildrenToAddAnimated) { + for (View child : mChildrenToAddAnimated) { mAnimationEvents.add(new AnimationEvent(child, AnimationEvent.ANIMATION_TYPE_ADD)); } @@ -966,6 +1016,22 @@ public class NotificationStackScrollLayout extends ViewGroup mTopPaddingNeedsAnimation = false; } + private void generateActivateEvent() { + if (mActivateNeedsAnimation) { + mAnimationEvents.add( + new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD)); + } + mActivateNeedsAnimation = false; + } + + private void generateDimmedEvent() { + if (mDimmedNeedsAnimation) { + mAnimationEvents.add( + new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED)); + } + mDimmedNeedsAnimation = false; + } + private boolean onInterceptTouchEventScroll(MotionEvent ev) { /* * This method JUST determines whether we want to intercept the motion. @@ -1074,6 +1140,13 @@ public class NotificationStackScrollLayout extends ViewGroup return mIsBeingDragged; } + /** + * @return Whether the specified motion event is actually happening over the content. + */ + private boolean isInContentBounds(MotionEvent event) { + return event.getY() < getHeight() - getEmptyBottomMargin(); + } + private void setIsBeingDragged(boolean isDragged) { mIsBeingDragged = isDragged; if (isDragged) { @@ -1131,14 +1204,12 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public void onHeightChanged(ExpandableView view) { - if (mListenForHeightChanges && !isCurrentlyAnimating()) { - updateContentHeight(); - updateScrollPositionIfNecessary(); - if (mOnHeightChangedListener != null) { - mOnHeightChangedListener.onHeightChanged(view); - } - requestChildrenUpdate(); + updateContentHeight(); + updateScrollPositionIfNecessary(); + if (mOnHeightChangedListener != null) { + mOnHeightChangedListener.onHeightChanged(view); } + requestChildrenUpdate(); } public void setOnHeightChangedListener( @@ -1151,10 +1222,34 @@ public class NotificationStackScrollLayout extends ViewGroup mAnimationEvents.clear(); } + /** + * See {@link AmbientState#setDimmed}. + */ + public void setDimmed(boolean dimmed, boolean animate) { + mAmbientState.setDimmed(dimmed); + if (animate) { + mDimmedNeedsAnimation = true; + mNeedsAnimation = true; + } + requestChildrenUpdate(); + } + + /** + * See {@link AmbientState#setActivatedChild}. + */ + public void setActivatedChild(View activatedChild) { + mAmbientState.setActivatedChild(activatedChild); + mActivateNeedsAnimation = true; + mNeedsAnimation = true; + requestChildrenUpdate(); + } + + public View getActivatedChild() { + return mAmbientState.getActivatedChild(); + } + private void applyCurrentState() { - mListenForHeightChanges = false; mCurrentStackScrollState.apply(); - mListenForHeightChanges = true; if (mListener != null) { mListener.onChildLocationsChanged(this); } @@ -1169,19 +1264,120 @@ public class NotificationStackScrollLayout extends ViewGroup static class AnimationEvent { - static int ANIMATION_TYPE_ADD = 1; - static int ANIMATION_TYPE_REMOVE = 2; - static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 3; - static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 4; + static AnimationFilter[] FILTERS = new AnimationFilter[] { + + // ANIMATION_TYPE_ADD + new AnimationFilter() + .animateAlpha() + .animateHeight() + .animateY() + .animateZ(), + + // ANIMATION_TYPE_REMOVE + new AnimationFilter() + .animateAlpha() + .animateHeight() + .animateY() + .animateZ(), + + // ANIMATION_TYPE_REMOVE_SWIPED_OUT + new AnimationFilter() + .animateAlpha() + .animateHeight() + .animateY() + .animateZ(), + + // ANIMATION_TYPE_TOP_PADDING_CHANGED + new AnimationFilter() + .animateAlpha() + .animateHeight() + .animateY() + .animateDimmed() + .animateScale() + .animateZ(), + + // ANIMATION_TYPE_START_DRAG + new AnimationFilter() + .animateAlpha(), + + // ANIMATION_TYPE_SNAP_BACK + new AnimationFilter() + .animateAlpha(), + + // ANIMATION_TYPE_ACTIVATED_CHILD + new AnimationFilter() + .animateScale() + .animateAlpha(), + + // ANIMATION_TYPE_DIMMED + new AnimationFilter() + .animateScale() + .animateDimmed() + }; + + static int[] LENGTHS = new int[] { + + // ANIMATION_TYPE_ADD + StackStateAnimator.ANIMATION_DURATION_STANDARD, + + // ANIMATION_TYPE_REMOVE + StackStateAnimator.ANIMATION_DURATION_STANDARD, + + // ANIMATION_TYPE_REMOVE_SWIPED_OUT + StackStateAnimator.ANIMATION_DURATION_STANDARD, + + // ANIMATION_TYPE_TOP_PADDING_CHANGED + StackStateAnimator.ANIMATION_DURATION_STANDARD, + + // ANIMATION_TYPE_START_DRAG + StackStateAnimator.ANIMATION_DURATION_STANDARD, + + // ANIMATION_TYPE_SNAP_BACK + StackStateAnimator.ANIMATION_DURATION_STANDARD, + + // ANIMATION_TYPE_ACTIVATED_CHILD + StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED, + + // ANIMATION_TYPE_DIMMED + StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED, + }; + + static int ANIMATION_TYPE_ADD = 0; + static int ANIMATION_TYPE_REMOVE = 1; + static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2; + static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3; + static int ANIMATION_TYPE_START_DRAG = 4; + static int ANIMATION_TYPE_SNAP_BACK = 5; + static int ANIMATION_TYPE_ACTIVATED_CHILD = 6; + static int ANIMATION_TYPE_DIMMED = 7; final long eventStartTime; final View changingView; final int animationType; + final AnimationFilter filter; + final long length; AnimationEvent(View view, int type) { eventStartTime = AnimationUtils.currentAnimationTimeMillis(); changingView = view; animationType = type; + filter = FILTERS[type]; + length = LENGTHS[type]; + } + + /** + * Combines the length of several animation events into a single value. + * + * @param events The events of the lengths to combine. + * @return The combined length. This is just the maximum of all length. + */ + static long combineLength(ArrayList<AnimationEvent> events) { + long length = 0; + int size = events.size(); + for (int i = 0; i < size; i++) { + length = Math.max(length, events.get(i).length); + } + return length; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 09d8d50..5e4d496 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -39,6 +39,10 @@ public class StackScrollAlgorithm { private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3; private static final int MAX_ITEMS_IN_TOP_STACK = 3; + /** When a child is activated, the other cards' alpha fade to this value. */ + private static final float ACTIVATED_INVERSE_ALPHA = 0.9f; + private static final float DIMMED_SCALE = 0.95f; + private int mPaddingBetweenElements; private int mCollapsedSize; private int mTopStackPeekSize; @@ -92,7 +96,7 @@ public class StackScrollAlgorithm { } - public void getStackScrollState(StackScrollState resultState) { + public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) { // The state of the local variables are saved in an algorithmState to easily subdivide it // into multiple phases. StackScrollAlgorithmState algorithmState = mTempAlgorithmState; @@ -106,7 +110,7 @@ public class StackScrollAlgorithm { algorithmState.scrolledPixelsTop = 0; algorithmState.itemsInBottomStack = 0.0f; algorithmState.partialInBottom = 0.0f; - algorithmState.scrollY = resultState.getScrollY() + mCollapsedSize; + algorithmState.scrollY = ambientState.getScrollY() + mCollapsedSize; updateVisibleChildren(resultState, algorithmState); @@ -118,6 +122,57 @@ public class StackScrollAlgorithm { // Phase 3: updateZValuesForState(resultState, algorithmState); + + handleDraggedViews(ambientState, resultState, algorithmState); + updateDimmedActivated(ambientState, resultState, algorithmState); + } + + /** + * Updates the dimmed and activated states of the children. + */ + private void updateDimmedActivated(AmbientState ambientState, StackScrollState resultState, + StackScrollAlgorithmState algorithmState) { + boolean dimmed = ambientState.isDimmed(); + View activatedChild = ambientState.getActivatedChild(); + int childCount = algorithmState.visibleChildren.size(); + for (int i = 0; i < childCount; i++) { + View child = algorithmState.visibleChildren.get(i); + StackScrollState.ViewState childViewState = resultState.getViewStateForView(child); + childViewState.dimmed = dimmed; + childViewState.scale = !dimmed || activatedChild == child + ? 1.0f + : DIMMED_SCALE; + if (dimmed && activatedChild != null && child != activatedChild) { + childViewState.alpha *= ACTIVATED_INVERSE_ALPHA; + } + } + } + + /** + * Handle the special state when views are being dragged + */ + private void handleDraggedViews(AmbientState ambientState, StackScrollState resultState, + StackScrollAlgorithmState algorithmState) { + ArrayList<View> draggedViews = ambientState.getDraggedViews(); + for (View draggedView : draggedViews) { + int childIndex = algorithmState.visibleChildren.indexOf(draggedView); + if (childIndex >= 0 && childIndex < algorithmState.visibleChildren.size() - 1) { + View nextChild = algorithmState.visibleChildren.get(childIndex + 1); + if (!draggedViews.contains(nextChild)) { + // only if the view is not dragged itself we modify its state to be fully + // visible + StackScrollState.ViewState viewState = resultState.getViewStateForView( + nextChild); + // The child below the dragged one must be fully visible + viewState.alpha = 1; + } + + // Lets set the alpha to the one it currently has, as its currently being dragged + StackScrollState.ViewState viewState = resultState.getViewStateForView(draggedView); + // The dragged child should keep the set alpha + viewState.alpha = draggedView.getAlpha(); + } + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index 26cef36..8fc26d8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -37,19 +37,10 @@ public class StackScrollState { private final ViewGroup mHostView; private Map<ExpandableView, ViewState> mStateMap; - private int mScrollY; private final Rect mClipRect = new Rect(); private int mBackgroundRoundedRectCornerRadius; private final Outline mChildOutline = new Outline(); - public int getScrollY() { - return mScrollY; - } - - public void setScrollY(int scrollY) { - this.mScrollY = scrollY; - } - public StackScrollState(ViewGroup hostView) { mHostView = hostView; mStateMap = new HashMap<ExpandableView, ViewState>(); @@ -93,6 +84,7 @@ public class StackScrollState { int numChildren = mHostView.getChildCount(); float previousNotificationEnd = 0; float previousNotificationStart = 0; + boolean previousNotificationIsSwiped = false; for (int i = 0; i < numChildren; i++) { ExpandableView child = (ExpandableView) mHostView.getChildAt(i); ViewState state = mStateMap.get(child); @@ -105,10 +97,12 @@ public class StackScrollState { float alpha = child.getAlpha(); float yTranslation = child.getTranslationY(); float zTranslation = child.getTranslationZ(); + float scale = child.getScaleX(); int height = child.getActualHeight(); float newAlpha = state.alpha; float newYTranslation = state.yTranslation; float newZTranslation = state.zTranslation; + float newScale = state.scale; int newHeight = state.height; boolean becomesInvisible = newAlpha == 0.0f; if (alpha != newAlpha) { @@ -146,19 +140,36 @@ public class StackScrollState { child.setTranslationZ(newZTranslation); } + // apply scale + if (scale != newScale) { + child.setScaleX(newScale); + child.setScaleY(newScale); + } + // apply height if (height != newHeight) { - child.setActualHeight(newHeight); + child.setActualHeight(newHeight, false /* notifyListeners */); } + // apply dimming + child.setDimmed(state.dimmed, false /* animate */); + // apply clipping and shadow float newNotificationEnd = newYTranslation + newHeight; + + // When the previous notification is swiped, we don't clip the content to the + // bottom of it. + float clipHeight = previousNotificationIsSwiped + ? newHeight + : newNotificationEnd - (previousNotificationEnd); + updateChildClippingAndBackground(child, newHeight, - newNotificationEnd - (previousNotificationEnd), + clipHeight, (int) (newHeight - (previousNotificationStart - newYTranslation))); - previousNotificationStart = newYTranslation; + previousNotificationStart = newYTranslation + child.getClipTopAmount(); previousNotificationEnd = newNotificationEnd; + previousNotificationIsSwiped = child.getTranslationX() != 0; } } } @@ -219,6 +230,8 @@ public class StackScrollState { float zTranslation; int height; boolean gone; + float scale; + boolean dimmed; /** * The location this view is currently rendered at. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 4dce288..ca383aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -16,144 +16,519 @@ package com.android.systemui.statusbar.stack; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; + +import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; /** * An stack state animator which handles animations to new StackScrollStates */ public class StackStateAnimator { - private static final int ANIMATION_DURATION = 360; + public static final int ANIMATION_DURATION_STANDARD = 360; + public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; + + private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; + private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; + private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag; + private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; + private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; + private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; + private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; + private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; + private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag; + private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; + private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; + private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; + private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; + private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; + private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag; + private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; + private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; + private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; private final Interpolator mFastOutSlowInInterpolator; public NotificationStackScrollLayout mHostLayout; - private boolean mAnimationIsRunning; private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents = new ArrayList<>(); + private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = + new ArrayList<>(); + private Set<Animator> mAnimatorSet = new HashSet<Animator>(); + private Stack<AnimatorListenerAdapter> mAnimationListenerPool + = new Stack<AnimatorListenerAdapter>(); + private AnimationFilter mAnimationFilter = new AnimationFilter(); + private long mCurrentLength; public StackStateAnimator(NotificationStackScrollLayout hostLayout) { mHostLayout = hostLayout; mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), - android.R.interpolator.fast_out_slow_in); + android.R.interpolator.fast_out_slow_in); } public boolean isRunning() { - return mAnimationIsRunning; + return !mAnimatorSet.isEmpty(); } public void startAnimationForEvents( ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, StackScrollState finalState) { - int numEvents = mAnimationEvents.size(); - if (numEvents == 0) { - // No events, so we don't perform any animation - return; - } - long lastEventStartTime = mAnimationEvents.get(numEvents - 1).eventStartTime; - long eventEnd = lastEventStartTime + ANIMATION_DURATION; - long currentTime = AnimationUtils.currentAnimationTimeMillis(); - long newDuration = eventEnd - currentTime; - if (newDuration <= 0) { - // last event is long before this, so we don't do anything - return; - } - initializeAddedViewStates(mAnimationEvents, finalState); + + processAnimationEvents(mAnimationEvents, finalState); + int childCount = mHostLayout.getChildCount(); - boolean isFirstAnimatingView = true; + mAnimationFilter.applyCombination(mNewEvents); + mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents); for (int i = 0; i < childCount; i++) { final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); StackScrollState.ViewState viewState = finalState.getViewStateForView(child); if (viewState == null) { continue; } - int childVisibility = child.getVisibility(); - boolean wasVisible = childVisibility == View.VISIBLE; - final float alpha = viewState.alpha; - if (!wasVisible && alpha != 0 && !viewState.gone) { - child.setVisibility(View.VISIBLE); - } - startPropertyAnimation(newDuration, isFirstAnimatingView, child, viewState, alpha); + startAnimations(child, viewState); - // TODO: animate clipBounds child.setClipBounds(null); - int currentHeigth = child.getActualHeight(); - if (viewState.height != currentHeigth) { - startHeightAnimation(newDuration, child, viewState, currentHeigth); - } - isFirstAnimatingView = false; } - mAnimationIsRunning = true; + if (!isRunning()) { + // no child has preformed any animation, lets finish + onAnimationFinished(); + } } - private void startPropertyAnimation(long newDuration, final boolean hasFinishAction, - final ExpandableView child, StackScrollState.ViewState viewState, final float alpha) { - child.animate().setInterpolator(mFastOutSlowInInterpolator) - .translationY(viewState.yTranslation) - .translationZ(viewState.zTranslation) - .setDuration(newDuration) - .withEndAction(new Runnable() { - @Override - public void run() { - mAnimationIsRunning = false; - if (hasFinishAction) { - mHandledEvents.clear(); - mHostLayout.onChildAnimationFinished(); - } - if (alpha == 0) { - child.setVisibility(View.INVISIBLE); - } - } - }); + /** + * Start an animation to the given viewState + */ + private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) { + int childVisibility = child.getVisibility(); + boolean wasVisible = childVisibility == View.VISIBLE; + final float alpha = viewState.alpha; + if (!wasVisible && alpha != 0 && !viewState.gone) { + child.setVisibility(View.VISIBLE); + } + // start translationY animation + if (child.getTranslationY() != viewState.yTranslation) { + startYTranslationAnimation(child, viewState); + } + // start translationZ animation + if (child.getTranslationZ() != viewState.zTranslation) { + startZTranslationAnimation(child, viewState); + } + // start scale animation + if (child.getScaleX() != viewState.scale) { + startScaleAnimation(child, viewState); + } + // start alpha animation if (alpha != child.getAlpha()) { - child.animate().withLayer().alpha(alpha); + startAlphaAnimation(child, viewState); } + // start height animation + if (viewState.height != child.getActualHeight()) { + startHeightAnimation(child, viewState); + } + // start dimmed animation + child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed); } - private void startHeightAnimation(long newDuration, final ExpandableView child, - StackScrollState.ViewState viewState, int currentHeigth) { - ValueAnimator heightAnimator = ValueAnimator.ofInt(currentHeigth, viewState.height); - heightAnimator.setInterpolator(mFastOutSlowInInterpolator); - heightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + private void startHeightAnimation(final ExpandableView child, + StackScrollState.ViewState viewState) { + Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); + Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); + int newEndValue = viewState.height; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); + if (!mAnimationFilter.animateHeight) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + int relativeDiff = newEndValue - previousEndValue; + int newStartValue = previousStartValue + relativeDiff; + values[0].setIntValues(newStartValue, newEndValue); + child.setTag(TAG_START_HEIGHT, newStartValue); + child.setTag(TAG_END_HEIGHT, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setActualHeight(newEndValue, false); + return; + } + } + + ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - child.setActualHeight((int) animation.getAnimatedValue()); + child.setActualHeight((int) animation.getAnimatedValue(), + false /* notifyListeners */); } }); - heightAnimator.setDuration(newDuration); - heightAnimator.start(); + animator.setInterpolator(mFastOutSlowInInterpolator); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_HEIGHT, null); + child.setTag(TAG_START_HEIGHT, null); + child.setTag(TAG_END_HEIGHT, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_HEIGHT, animator); + child.setTag(TAG_START_HEIGHT, child.getActualHeight()); + child.setTag(TAG_END_HEIGHT, newEndValue); + } + + private void startAlphaAnimation(final ExpandableView child, + final StackScrollState.ViewState viewState) { + Float previousStartValue = getChildTag(child,TAG_START_ALPHA); + Float previousEndValue = getChildTag(child,TAG_END_ALPHA); + final float newEndValue = viewState.alpha; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); + if (!mAnimationFilter.animateAlpha) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_ALPHA, newStartValue); + child.setTag(TAG_END_ALPHA, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setAlpha(newEndValue); + if (newEndValue == 0) { + child.setVisibility(View.INVISIBLE); + } + } + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, + child.getAlpha(), newEndValue); + animator.setInterpolator(mFastOutSlowInInterpolator); + // Handle layer type + final int currentLayerType = child.getLayerType(); + child.setLayerType(View.LAYER_TYPE_HARDWARE, null); + animator.addListener(new AnimatorListenerAdapter() { + public boolean mWasCancelled; + + @Override + public void onAnimationEnd(Animator animation) { + child.setLayerType(currentLayerType, null); + if (newEndValue == 0 && !mWasCancelled) { + child.setVisibility(View.INVISIBLE); + } + child.setTag(TAG_ANIMATOR_ALPHA, null); + child.setTag(TAG_START_ALPHA, null); + child.setTag(TAG_END_ALPHA, null); + } + + @Override + public void onAnimationCancel(Animator animation) { + mWasCancelled = true; + } + + @Override + public void onAnimationStart(Animator animation) { + mWasCancelled = false; + } + }); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_ALPHA, animator); + child.setTag(TAG_START_ALPHA, child.getAlpha()); + child.setTag(TAG_END_ALPHA, newEndValue); + } + + private void startZTranslationAnimation(final ExpandableView child, + final StackScrollState.ViewState viewState) { + Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); + float newEndValue = viewState.zTranslation; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); + if (!mAnimationFilter.animateZ) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_TRANSLATION_Z, newStartValue); + child.setTag(TAG_END_TRANSLATION_Z, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setTranslationZ(newEndValue); + } + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, + child.getTranslationZ(), newEndValue); + animator.setInterpolator(mFastOutSlowInInterpolator); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); + child.setTag(TAG_START_TRANSLATION_Z, null); + child.setTag(TAG_END_TRANSLATION_Z, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); + child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); + child.setTag(TAG_END_TRANSLATION_Z, newEndValue); + } + + private void startYTranslationAnimation(final ExpandableView child, + StackScrollState.ViewState viewState) { + Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); + float newEndValue = viewState.yTranslation; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); + if (!mAnimationFilter.animateY) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_TRANSLATION_Y, newStartValue); + child.setTag(TAG_END_TRANSLATION_Y, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setTranslationY(newEndValue); + return; + } + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, + child.getTranslationY(), newEndValue); + animator.setInterpolator(mFastOutSlowInInterpolator); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); + child.setTag(TAG_START_TRANSLATION_Y, null); + child.setTag(TAG_END_TRANSLATION_Y, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); + child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); + child.setTag(TAG_END_TRANSLATION_Y, newEndValue); + } + + private void startScaleAnimation(final ExpandableView child, + StackScrollState.ViewState viewState) { + Float previousStartValue = getChildTag(child, TAG_START_SCALE); + Float previousEndValue = getChildTag(child, TAG_END_SCALE); + float newEndValue = viewState.scale; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE); + if (!mAnimationFilter.animateScale) { + // just a local update was performed + if (previousAnimator != null) { + // we need to increase all animation keyframes of the previous animator by the + // relative change to the end value + PropertyValuesHolder[] values = previousAnimator.getValues(); + float relativeDiff = newEndValue - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + values[0].setFloatValues(newStartValue, newEndValue); + values[1].setFloatValues(newStartValue, newEndValue); + child.setTag(TAG_START_SCALE, newStartValue); + child.setTag(TAG_END_SCALE, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setScaleX(newEndValue); + child.setScaleY(newEndValue); + } + } + + PropertyValuesHolder holderX = + PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue); + PropertyValuesHolder holderY = + PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue); + ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY); + animator.setInterpolator(mFastOutSlowInInterpolator); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_SCALE, null); + child.setTag(TAG_START_SCALE, null); + child.setTag(TAG_END_SCALE, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_SCALE, animator); + child.setTag(TAG_START_SCALE, child.getScaleX()); + child.setTag(TAG_END_SCALE, newEndValue); + } + + /** + * Start an animator instantly instead of waiting on the next synchronization frame + */ + private void startInstantly(ValueAnimator animator) { + animator.start(); + animator.setCurrentPlayTime(0); } /** - * Initialize the viewStates for the added children + * @return an adapter which ensures that onAnimationFinished is called once no animation is + * running anymore + */ + private AnimatorListenerAdapter getGlobalAnimationFinishedListener() { + if (!mAnimationListenerPool.empty()) { + return mAnimationListenerPool.pop(); + } + + // We need to create a new one, no reusable ones found + return new AnimatorListenerAdapter() { + private boolean mWasCancelled; + + @Override + public void onAnimationEnd(Animator animation) { + mAnimatorSet.remove(animation); + if (mAnimatorSet.isEmpty() && !mWasCancelled) { + onAnimationFinished(); + } + mAnimationListenerPool.push(this); + } + + @Override + public void onAnimationCancel(Animator animation) { + mWasCancelled = true; + } + + @Override + public void onAnimationStart(Animator animation) { + mAnimatorSet.add(animation); + mWasCancelled = false; + } + }; + } + + private <T> T getChildTag(View child, int tag) { + return (T) child.getTag(tag); + } + + /** + * Cancel the previous animator and get the duration of the new animation. + * + * @param previousAnimator the animator which was running before + * @return the new duration + */ + private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator) { + long newDuration = mCurrentLength; + if (previousAnimator != null) { + // We take either the desired length of the new animation or the remaining time of + // the previous animator, whichever is longer. + newDuration = Math.max(previousAnimator.getDuration() + - previousAnimator.getCurrentPlayTime(), newDuration); + previousAnimator.cancel(); + } + return newDuration; + } + + private void onAnimationFinished() { + mHandledEvents.clear(); + mNewEvents.clear(); + mHostLayout.onChildAnimationFinished(); + } + + /** + * Process the animationEvents for a new animation * - * @param animationEvents the animation events who contain the added children + * @param animationEvents the animation events for the animation to perform * @param finalState the final state to animate to */ - private void initializeAddedViewStates( + private void processAnimationEvents( ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, StackScrollState finalState) { + mNewEvents.clear(); for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) { View changingView = event.changingView; - if (event.animationType == NotificationStackScrollLayout.AnimationEvent - .ANIMATION_TYPE_ADD && !mHandledEvents.contains(event)) { - - // This item is added, initialize it's properties. - StackScrollState.ViewState viewState = finalState.getViewStateForView(changingView); - if (viewState == null) { - // The position for this child was never generated, let's continue. - continue; + if (!mHandledEvents.contains(event)) { + if (event.animationType == NotificationStackScrollLayout.AnimationEvent + .ANIMATION_TYPE_ADD) { + + // This item is added, initialize it's properties. + StackScrollState.ViewState viewState = finalState + .getViewStateForView(changingView); + if (viewState == null) { + // The position for this child was never generated, let's continue. + continue; + } + changingView.setAlpha(0); + changingView.setTranslationY(viewState.yTranslation); + changingView.setTranslationZ(viewState.zTranslation); } - changingView.setAlpha(0); - changingView.setTranslationY(viewState.yTranslation); - changingView.setTranslationZ(viewState.zTranslation); mHandledEvents.add(event); + mNewEvents.add(event); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index d615542..9006c9a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -78,7 +78,8 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { + public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { } @Override @@ -120,11 +121,6 @@ public class TvStatusBar extends BaseStatusBar { } @Override - protected int getExpandedViewMaxHeight() { - return 0; - } - - @Override protected boolean shouldDisableNavbarGestures() { return true; } @@ -154,4 +150,11 @@ public class TvStatusBar extends BaseStatusBar { protected void refreshLayout(int layoutDirection) { } + @Override + public void onActivated(View view) { + } + + @Override + public void onActivationReset(View view) { + } } diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index dce4f58..a62d1fd 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -63,6 +63,25 @@ public class RenderScript { static Method registerNativeAllocation; static Method registerNativeFree; + /* + * Context creation flag which specifies a normal context. + */ + public static final long CREATE_FLAG_NONE = 0x0000; + + /* + * Context creation flag which specifies a context optimized for low + * latency over peak performance. This is a hint and may have no effect + * on some implementations. + */ + public static final long CREATE_FLAG_LOW_LATENCY = 0x0001; + + /* + * Context creation flag which specifies a context optimized for long + * battery life over peak performance. This is a hint and may have no effect + * on some implementations. + */ + public static final long CREATE_FLAG_LOW_POWER = 0x0002; + static { sInitialized = false; if (!SystemProperties.getBoolean("config.disable_renderscript", false)) { @@ -1145,7 +1164,7 @@ public class RenderScript { * @hide */ public static RenderScript create(Context ctx, int sdkVersion) { - return create(ctx, sdkVersion, ContextType.NORMAL); + return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE); } /** @@ -1155,7 +1174,7 @@ public class RenderScript { * @param ctx The context. * @return RenderScript */ - public static RenderScript create(Context ctx, int sdkVersion, ContextType ct) { + public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, long flags) { if (!sInitialized) { Log.e(LOG_TAG, "RenderScript.create() called when disabled; someone is likely to crash"); return null; @@ -1194,7 +1213,21 @@ public class RenderScript { */ public static RenderScript create(Context ctx, ContextType ct) { int v = ctx.getApplicationInfo().targetSdkVersion; - return create(ctx, v, ct); + return create(ctx, v, ct, CREATE_FLAG_NONE); + } + + /** + * Create a RenderScript context. + * + * + * @param ctx The context. + * @param ct The type of context to be created. + * @param flags The OR of the CREATE_FLAG_* options desired + * @return RenderScript + */ + public static RenderScript create(Context ctx, ContextType ct, long flags) { + int v = ctx.getApplicationInfo().targetSdkVersion; + return create(ctx, v, ct, flags); } /** diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk index 07933b4..f1ddc07 100644 --- a/rs/jni/Android.mk +++ b/rs/jni/Android.mk @@ -27,7 +27,6 @@ LOCAL_C_INCLUDES += \ LOCAL_CFLAGS += -Wno-unused-parameter -LOCAL_LDLIBS := -lpthread LOCAL_ADDITIONAL_DEPENDENCIES := $(addprefix $(rs_generated_include_dir)/,rsgApiFuncDecl.h) LOCAL_MODULE:= librs_jni LOCAL_ADDITIONAL_DEPENDENCIES += $(rs_generated_source) diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 45cdb65..dfffa8a 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -74,7 +74,7 @@ import android.net.NetworkStateTracker; import android.net.NetworkUtils; import android.net.Proxy; import android.net.ProxyDataTracker; -import android.net.ProxyProperties; +import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.SamplingDataTracker; import android.net.Uri; @@ -406,12 +406,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { private ArrayList mInetLog; // track the current default http proxy - tell the world if we get a new one (real change) - private ProxyProperties mDefaultProxy = null; + private ProxyInfo mDefaultProxy = null; private Object mProxyLock = new Object(); private boolean mDefaultProxyDisabled = false; // track the global proxy. - private ProxyProperties mGlobalProxy = null; + private ProxyInfo mGlobalProxy = null; private PacManager mPacManager = null; @@ -3192,7 +3192,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { break; } case EVENT_PROXY_HAS_CHANGED: { - handleApplyDefaultProxy((ProxyProperties)msg.obj); + handleApplyDefaultProxy((ProxyInfo)msg.obj); break; } } @@ -3410,19 +3410,19 @@ public class ConnectivityService extends IConnectivityManager.Stub { return; } - public ProxyProperties getProxy() { + public ProxyInfo getProxy() { // this information is already available as a world read/writable jvm property // so this API change wouldn't have a benifit. It also breaks the passing // of proxy info to all the JVMs. // enforceAccessPermission(); synchronized (mProxyLock) { - ProxyProperties ret = mGlobalProxy; + ProxyInfo ret = mGlobalProxy; if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy; return ret; } } - public void setGlobalProxy(ProxyProperties proxyProperties) { + public void setGlobalProxy(ProxyInfo proxyProperties) { enforceConnectivityInternalPermission(); synchronized (mProxyLock) { @@ -3435,18 +3435,18 @@ public class ConnectivityService extends IConnectivityManager.Stub { String exclList = ""; String pacFileUrl = ""; if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) || - !TextUtils.isEmpty(proxyProperties.getPacFileUrl()))) { + (proxyProperties.getPacFileUrl() != null))) { if (!proxyProperties.isValid()) { if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString()); return; } - mGlobalProxy = new ProxyProperties(proxyProperties); + mGlobalProxy = new ProxyInfo(proxyProperties); host = mGlobalProxy.getHost(); port = mGlobalProxy.getPort(); - exclList = mGlobalProxy.getExclusionList(); + exclList = mGlobalProxy.getExclusionListAsString(); if (proxyProperties.getPacFileUrl() != null) { - pacFileUrl = proxyProperties.getPacFileUrl(); + pacFileUrl = proxyProperties.getPacFileUrl().toString(); } } else { mGlobalProxy = null; @@ -3478,11 +3478,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST); String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC); if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) { - ProxyProperties proxyProperties; + ProxyInfo proxyProperties; if (!TextUtils.isEmpty(pacFileUrl)) { - proxyProperties = new ProxyProperties(pacFileUrl); + proxyProperties = new ProxyInfo(pacFileUrl); } else { - proxyProperties = new ProxyProperties(host, port, exclList); + proxyProperties = new ProxyInfo(host, port, exclList); } if (!proxyProperties.isValid()) { if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString()); @@ -3495,7 +3495,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - public ProxyProperties getGlobalProxy() { + public ProxyInfo getGlobalProxy() { // this information is already available as a world read/writable jvm property // so this API change wouldn't have a benifit. It also breaks the passing // of proxy info to all the JVMs. @@ -3505,9 +3505,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - private void handleApplyDefaultProxy(ProxyProperties proxy) { + private void handleApplyDefaultProxy(ProxyInfo proxy) { if (proxy != null && TextUtils.isEmpty(proxy.getHost()) - && TextUtils.isEmpty(proxy.getPacFileUrl())) { + && (proxy.getPacFileUrl() == null)) { proxy = null; } synchronized (mProxyLock) { @@ -3517,6 +3517,18 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (DBG) log("Invalid proxy properties, ignoring: " + proxy.toString()); return; } + + // This call could be coming from the PacManager, containing the port of the local + // proxy. If this new proxy matches the global proxy then copy this proxy to the + // global (to get the correct local port), and send a broadcast. + // TODO: Switch PacManager to have its own message to send back rather than + // reusing EVENT_HAS_CHANGED_PROXY and this call to handleApplyDefaultProxy. + if ((mGlobalProxy != null) && (proxy != null) && (proxy.getPacFileUrl() != null) + && proxy.getPacFileUrl().equals(mGlobalProxy.getPacFileUrl())) { + mGlobalProxy = proxy; + sendProxyBroadcast(mGlobalProxy); + return; + } mDefaultProxy = proxy; if (mGlobalProxy != null) return; @@ -3544,13 +3556,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { return; } } - ProxyProperties p = new ProxyProperties(data[0], proxyPort, ""); + ProxyInfo p = new ProxyInfo(data[0], proxyPort, ""); setGlobalProxy(p); } } - private void sendProxyBroadcast(ProxyProperties proxy) { - if (proxy == null) proxy = new ProxyProperties("", 0, ""); + private void sendProxyBroadcast(ProxyInfo proxy) { + if (proxy == null) proxy = new ProxyInfo("", 0, ""); if (mPacManager.setCurrentProxyScriptUrl(proxy)) return; if (DBG) log("sending Proxy Broadcast for " + proxy); Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 7ed1cc7..10315a7 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -1532,14 +1532,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mImeWindowVis = vis; mBackDisposition = backDisposition; - if (mStatusBar != null) { - mStatusBar.setImeWindowStatus(token, vis, backDisposition); - } final boolean iconVisibility = ((vis & (InputMethodService.IME_ACTIVE)) != 0) && (mWindowManagerService.isHardKeyboardAvailable() || (vis & (InputMethodService.IME_VISIBLE)) != 0); + final boolean needsToShowImeSwitcher = iconVisibility + && needsToShowImeSwitchOngoingNotification(); + if (mStatusBar != null) { + mStatusBar.setImeWindowStatus(token, vis, backDisposition, + needsToShowImeSwitcher); + } final InputMethodInfo imi = mMethodMap.get(mCurMethodId); - if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { + if (imi != null && needsToShowImeSwitcher) { // Used to load label final CharSequence title = mRes.getText( com.android.internal.R.string.select_input_method); diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 7ec9b82..d5f045e 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -201,8 +201,8 @@ class MountService extends IMountService.Stub public static final String[] CRYPTO_TYPES = { "password", "default", "pattern", "pin" }; - private Context mContext; - private NativeDaemonConnector mConnector; + private final Context mContext; + private final NativeDaemonConnector mConnector; private final Object mVolumesLock = new Object(); diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 132ca00..ea9de1e 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -41,6 +41,7 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; import android.view.InputDevice; +import android.media.AudioManager; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; @@ -73,6 +74,8 @@ public class VibratorService extends IVibratorService.Stub private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators private int mCurVibUid = -1; + private boolean mLowPowerMode; + private SettingsObserver mSettingObserver; native static boolean vibratorExists(); native static void vibratorOn(long milliseconds); @@ -159,15 +162,15 @@ public class VibratorService extends IVibratorService.Stub public void systemReady() { mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE); + mSettingObserver = new SettingsObserver(mH); mContext.getContentResolver().registerContentObserver( - Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true, - new ContentObserver(mH) { - @Override - public void onChange(boolean selfChange) { - updateInputDeviceVibrators(); - } - }, UserHandle.USER_ALL); + Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), + true, mSettingObserver, UserHandle.USER_ALL); + + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), false, + mSettingObserver, UserHandle.USER_ALL); mContext.registerReceiver(new BroadcastReceiver() { @Override @@ -179,6 +182,17 @@ public class VibratorService extends IVibratorService.Stub updateInputDeviceVibrators(); } + private final class SettingsObserver extends ContentObserver { + public SettingsObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean SelfChange) { + updateInputDeviceVibrators(); + } + } + public boolean hasVibrator() { return doVibratorExists(); } @@ -346,6 +360,9 @@ public class VibratorService extends IVibratorService.Stub // Lock held on mVibrations private void startVibrationLocked(final Vibration vib) { try { + if (mLowPowerMode && vib.mStreamHint != AudioManager.STREAM_RING) + return; + int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE, vib.mStreamHint, vib.mUid, vib.mOpPkg); if (mode == AppOpsManager.MODE_ALLOWED) { @@ -425,6 +442,9 @@ public class VibratorService extends IVibratorService.Stub } catch (SettingNotFoundException snfe) { } + mLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.LOW_POWER_MODE, 0) != 0; + if (mVibrateInputDevicesSetting) { if (!mInputDeviceListenerRegistered) { mInputDeviceListenerRegistered = true; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b7de325..09e7e12 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -136,7 +136,7 @@ import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Proxy; -import android.net.ProxyProperties; +import android.net.ProxyInfo; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -983,7 +983,7 @@ public final class ActivityManagerService extends ActivityManagerNative /** * This is set if we had to do a delayed dexopt of an app before launching - * it, to increasing the ANR timeouts in that case. + * it, to increase the ANR timeouts in that case. */ boolean mDidDexOpt; @@ -1323,7 +1323,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } break; case UPDATE_HTTP_PROXY_MSG: { - ProxyProperties proxy = (ProxyProperties)msg.obj; + ProxyInfo proxy = (ProxyInfo)msg.obj; String host = ""; String port = ""; String exclList = ""; @@ -1331,8 +1331,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (proxy != null) { host = proxy.getHost(); port = Integer.toString(proxy.getPort()); - exclList = proxy.getExclusionList(); - pacFileUrl = proxy.getPacFileUrl(); + exclList = proxy.getExclusionListAsString(); + pacFileUrl = proxy.getPacFileUrl().toString(); } synchronized (ActivityManagerService.this) { for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { @@ -9570,6 +9570,7 @@ public final class ActivityManagerService extends ActivityManagerNative } mAppOpsService.systemReady(); + mUsageStatsService.systemReady(); mSystemReady = true; } @@ -13763,7 +13764,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) { - ProxyProperties proxy = intent.getParcelableExtra("proxy"); + ProxyInfo proxy = intent.getParcelableExtra("proxy"); mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy)); } @@ -14391,6 +14392,7 @@ public final class ActivityManagerService extends ActivityManagerNative newConfig.seq = mConfigurationSeq; mConfiguration = newConfig; Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig); + mUsageStatsService.noteStartConfig(newConfig); final Configuration configCopy = new Configuration(mConfiguration); @@ -16528,7 +16530,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) { - if (userId != 0) { + if (userId != UserHandle.USER_OWNER) { Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntentLocked(null, null, intent, null, @@ -16557,6 +16559,8 @@ public final class ActivityManagerService extends ActivityManagerNative EventLogTags.writeAmSwitchUser(userId); getUserManagerLocked().userForeground(userId); sendUserSwitchBroadcastsLocked(oldUserId, userId); + } else { + mStackSupervisor.startBackgroundUserLocked(userId, uss); } if (needStart) { @@ -16732,7 +16736,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - void finishUserSwitch(UserStartedState uss) { + void finishUserBoot(UserStartedState uss) { synchronized (this) { if (uss.mState == UserStartedState.STATE_BOOTING && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) { @@ -16746,6 +16750,12 @@ public final class ActivityManagerService extends ActivityManagerNative android.Manifest.permission.RECEIVE_BOOT_COMPLETED, AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID, userId); } + } + } + + void finishUserSwitch(UserStartedState uss) { + synchronized (this) { + finishUserBoot(uss); startProfilesLocked(); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 7c3f288..efd2b57 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -17,7 +17,6 @@ package com.android.server.am; import android.os.Trace; -import com.android.internal.R.styleable; import com.android.internal.app.ResolverActivity; import com.android.server.AttributeCache; import com.android.server.am.ActivityStack.ActivityState; @@ -480,7 +479,7 @@ final class ActivityRecord { void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) { if (task != null && task.removeActivity(this)) { if (task != newTask) { - task.stack.removeTask(task, false); + task.stack.removeTask(task); } else { Slog.d(TAG, "!!! REMOVE THIS LOG !!! setTask: nearly removed stack=" + (newTask == null ? null : newTask.stack)); diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index ee39b67..442da31 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -36,8 +36,6 @@ import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES; import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; -import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE; - import android.service.voice.IVoiceInteractionSession; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; @@ -1082,20 +1080,6 @@ final class ActivityStack { } } - /** - * Version of ensureActivitiesVisible that can easily be called anywhere. - */ - final boolean ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) { - return ensureActivitiesVisibleLocked(starting, configChanges, false); - } - - final boolean ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, - boolean forceHomeShown) { - ActivityRecord r = topRunningActivityLocked(null); - return r != null && - ensureActivitiesVisibleLocked(r, starting, null, configChanges, forceHomeShown); - } - // Checks if any of the stacks above this one has a fullscreen activity behind it. // If so, this stack is hidden, otherwise it is visible. private boolean isStackVisible() { @@ -1107,16 +1091,26 @@ final class ActivityStack { return true; } - // Start at the task above this one and go up, looking for a visible - // fullscreen activity, or a translucent activity that requested the - // wallpaper to be shown behind it. + /** + * Start at the task above this one and go up, looking for a visible + * fullscreen activity, or a translucent activity that requested the + * wallpaper to be shown behind it. + */ for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) { final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks(); for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) { final ArrayList<ActivityRecord> activities = tasks.get(taskNdx).mActivities; for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) { final ActivityRecord r = activities.get(activityNdx); - if (!r.finishing && r.visible && r.fullscreen) { + + // Conditions for an activity to obscure the stack we're + // examining: + // 1. Not Finishing AND Visible AND: + // 2. Either: + // - Full Screen Activity OR + // - On top of Home and our stack is NOT home + if (!r.finishing && r.visible && (r.fullscreen || + (!isHomeStack() && r.frontOfTask && tasks.get(taskNdx).mOnTopOfHome))) { return false; } } @@ -1126,12 +1120,19 @@ final class ActivityStack { return true; } + final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) { + ActivityRecord r = topRunningActivityLocked(null); + if (r != null) { + ensureActivitiesVisibleLocked(r, starting, null, configChanges); + } + } + /** * Make sure that all activities that need to be visible (that is, they * currently can be seen by the user) actually are. */ - final boolean ensureActivitiesVisibleLocked(ActivityRecord top, ActivityRecord starting, - String onlyThisProcess, int configChanges, boolean forceHomeShown) { + final void ensureActivitiesVisibleLocked(ActivityRecord top, ActivityRecord starting, + String onlyThisProcess, int configChanges) { if (DEBUG_VISBILITY) Slog.v( TAG, "ensureActivitiesVisible behind " + top + " configChanges=0x" + Integer.toHexString(configChanges)); @@ -1149,7 +1150,6 @@ final class ActivityStack { // If the top activity is not fullscreen, then we need to // make sure any activities under it are now visible. boolean aboveTop = true; - boolean showHomeBehindStack = false; boolean behindFullscreen = !isStackVisible(); for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { @@ -1237,11 +1237,9 @@ final class ActivityStack { // At this point, nothing else needs to be shown if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r); behindFullscreen = true; - showHomeBehindStack = false; - } else if (isActivityOverHome(r)) { + } else if (!isHomeStack() && r.frontOfTask && task.mOnTopOfHome) { if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r); - showHomeBehindStack = true; - behindFullscreen = !isHomeStack() && r.frontOfTask && task.mOnTopOfHome; + behindFullscreen = true; } } else { if (DEBUG_VISBILITY) Slog.v( @@ -1291,7 +1289,6 @@ final class ActivityStack { } } } - return showHomeBehindStack; } void convertToTranslucent(ActivityRecord r) { @@ -2841,7 +2838,7 @@ final class ActivityStack { if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.mOnTopOfHome) { mStackSupervisor.moveHomeToTop(); } - removeTask(task, false); + removeTask(task); } cleanUpActivityServicesLocked(r); r.removeUriPermissionsLocked(); @@ -3717,7 +3714,7 @@ final class ActivityStack { return starting; } - void removeTask(TaskRecord task, boolean moving) { + void removeTask(TaskRecord task) { mStackSupervisor.endLockTaskModeIfTaskEnding(task); mWindowManager.removeTask(task.taskId); final ActivityRecord r = mResumedActivity; @@ -3731,14 +3728,20 @@ final class ActivityStack { mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true; } mTaskHistory.remove(task); - if (!moving && task.voiceSession != null) { - // This task was a voice interaction, so it should not remain on the - // recent tasks list. - try { - task.voiceSession.taskFinished(task.intent, task.taskId); - } catch (RemoteException e) { + + if (task.mActivities.isEmpty()) { + final boolean isVoiceSession = task.voiceSession != null; + if (isVoiceSession) { + try { + task.voiceSession.taskFinished(task.intent, task.taskId); + } catch (RemoteException e) { + } + } + if (task.autoRemoveFromRecents() || isVoiceSession) { + // Task creator asked to remove this when done, or this task was a voice + // interaction, so it should not remain on the recent tasks list. + mService.mRecentTasks.remove(task); } - mService.mRecentTasks.remove(task); } if (mTaskHistory.isEmpty()) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 2e979d2..ce3d853 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -193,6 +193,9 @@ public final class ActivityStackSupervisor implements DisplayListener { /** Used on user changes */ final ArrayList<UserStartedState> mStartingUsers = new ArrayList<UserStartedState>(); + /** Used to queue up any background users being started */ + final ArrayList<UserStartedState> mStartingBackgroundUsers = new ArrayList<UserStartedState>(); + /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity * is being brought in front of us. */ boolean mUserLeaving = false; @@ -1471,6 +1474,17 @@ public final class ActivityStackSupervisor implements DisplayListener { } } + switch (r.info.documentLaunchMode) { + case ActivityInfo.DOCUMENT_LAUNCH_NONE: + break; + case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS: + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + break; + case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING: + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + break; + } final boolean newDocument = intent.isDocument(); if (sourceRecord == null) { // This activity is not being started from another... in this @@ -1988,9 +2002,20 @@ public final class ActivityStackSupervisor implements DisplayListener { if (booting) { mService.finishBooting(); - } else if (startingUsers != null) { - for (int i = 0; i < startingUsers.size(); i++) { - mService.finishUserSwitch(startingUsers.get(i)); + } else { + // Complete user switch + if (startingUsers != null) { + for (int i = 0; i < startingUsers.size(); i++) { + mService.finishUserSwitch(startingUsers.get(i)); + } + } + // Complete starting up of background users + if (mStartingBackgroundUsers.size() > 0) { + startingUsers = new ArrayList<UserStartedState>(mStartingBackgroundUsers); + mStartingBackgroundUsers.clear(); + for (int i = 0; i < startingUsers.size(); i++) { + mService.finishUserBoot(startingUsers.get(i)); + } } } @@ -2237,7 +2262,7 @@ public final class ActivityStackSupervisor implements DisplayListener { Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId); return; } - task.stack.removeTask(task, true); + task.stack.removeTask(task); stack.addTask(task, toTop, true); mWindowManager.addTask(taskId, stackId, toTop); resumeTopActivitiesLocked(); @@ -2430,21 +2455,12 @@ public final class ActivityStackSupervisor implements DisplayListener { void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) { // First the front stacks. In case any are not fullscreen and are in front of home. - boolean showHomeBehindStack = false; for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; final int topStackNdx = stacks.size() - 1; for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) { final ActivityStack stack = stacks.get(stackNdx); - if (stackNdx == topStackNdx) { - // Top stack. - showHomeBehindStack = - stack.ensureActivitiesVisibleLocked(starting, configChanges); - } else { - // Back stack. - stack.ensureActivitiesVisibleLocked(starting, configChanges, - showHomeBehindStack); - } + stack.ensureActivitiesVisibleLocked(starting, configChanges); } } } @@ -2496,6 +2512,15 @@ public final class ActivityStackSupervisor implements DisplayListener { return homeInFront; } + /** + * Add background users to send boot completed events to. + * @param userId The user being started in the background + * @param uss The state object for the user. + */ + public void startBackgroundUserLocked(int userId, UserStartedState uss) { + mStartingBackgroundUsers.add(uss); + } + final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) { int N = mStoppingActivities.size(); if (N <= 0) return null; diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 9f0bc10..862932c 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -139,6 +139,9 @@ final class TaskRecord extends ThumbnailHolder { userId = UserHandle.getUserId(info.applicationInfo.uid); creatorUid = info.applicationInfo.uid; + if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) { + intent.addFlags(Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS); + } } void disposeThumbnail() { @@ -246,6 +249,11 @@ final class TaskRecord extends ThumbnailHolder { return mActivities.size() == 0; } + boolean autoRemoveFromRecents() { + return intent != null && + (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0; + } + /** * Completely remove all activities associated with an existing * task starting at a specified index. diff --git a/services/core/java/com/android/server/am/UsageStatsService.java b/services/core/java/com/android/server/am/UsageStatsService.java index 42cf900..4a5a554 100644 --- a/services/core/java/com/android/server/am/UsageStatsService.java +++ b/services/core/java/com/android/server/am/UsageStatsService.java @@ -17,26 +17,31 @@ package com.android.server.am; import android.app.AppGlobals; +import android.app.AppOpsManager; +import android.app.UsageStats; import android.content.ComponentName; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.os.Binder; import android.os.IBinder; import android.os.FileUtils; import android.os.Parcel; +import android.os.ParcelableParcel; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Slog; +import android.util.TimeUtils; import android.util.Xml; import com.android.internal.app.IUsageStats; import com.android.internal.content.PackageMonitor; -import com.android.internal.os.PkgUsageStats; import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; @@ -46,7 +51,6 @@ import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; @@ -55,8 +59,6 @@ import java.util.Calendar; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -75,7 +77,7 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; // Current on-disk Parcel version - private static final int VERSION = 1008; + private static final int VERSION = 1010; private static final int CHECKIN_VERSION = 4; @@ -94,13 +96,10 @@ public final class UsageStatsService extends IUsageStats.Stub { static IUsageStats sService; private Context mContext; - // structure used to maintain statistics since the last checkin. - final private ArrayMap<String, PkgUsageStatsExtended> mStats - = new ArrayMap<String, PkgUsageStatsExtended>(); + private AppOpsManager mAppOps; - // Maintains the last time any component was resumed, for all time. - final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes - = new ArrayMap<String, ArrayMap<String, Long>>(); + // structure used to maintain statistics since the last checkin. + private LocalUsageStats mStats = new LocalUsageStats(); // To remove last-resume time stats when a pacakge is removed. private PackageMonitor mPackageMonitor; @@ -115,6 +114,7 @@ public final class UsageStatsService extends IUsageStats.Stub { private String mLastResumedPkg; private String mLastResumedComp; private boolean mIsResumed; + private ConfigUsageStatsExtended mCurrentConfigStats; private File mFile; private AtomicFile mHistoryFile; private String mFileLeaf; @@ -127,6 +127,30 @@ public final class UsageStatsService extends IUsageStats.Stub { private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0); private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false); + static class LocalUsageStats extends UsageStats { + public LocalUsageStats() { + } + public LocalUsageStats(Parcel in, boolean extended) { + super(in, extended); + } + @Override + public PackageStats onNewPackageStats(String pkgName) { + return new PkgUsageStatsExtended(pkgName); + } + @Override + public PackageStats onNewPackageStats(Parcel in) { + return new PkgUsageStatsExtended(in); + } + @Override + public ConfigurationStats onNewConfigurationStats(Configuration config) { + return new ConfigUsageStatsExtended(config); + } + @Override + public ConfigurationStats onNewConfigurationStats(Parcel source) { + return new ConfigUsageStatsExtended(source); + } + } + static class TimeStats { int mCount; final int[] mTimes = new int[NUM_LAUNCH_TIME_BINS]; @@ -166,27 +190,18 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - static class PkgUsageStatsExtended { + static class PkgUsageStatsExtended extends UsageStats.PackageStats { final ArrayMap<String, TimeStats> mLaunchTimes = new ArrayMap<String, TimeStats>(); final ArrayMap<String, TimeStats> mFullyDrawnTimes = new ArrayMap<String, TimeStats>(); - int mLaunchCount; - long mUsageTime; - long mPausedTime; - long mResumedTime; - PkgUsageStatsExtended() { - mLaunchCount = 0; - mUsageTime = 0; + PkgUsageStatsExtended(String pkgName) { + super(pkgName); } PkgUsageStatsExtended(Parcel in) { - mLaunchCount = in.readInt(); - mUsageTime = in.readLong(); - if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount - + ", Usage time:" + mUsageTime); - + super(in); final int numLaunchTimeStats = in.readInt(); if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats); mLaunchTimes.ensureCapacity(numLaunchTimeStats); @@ -208,18 +223,6 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void updateResume(String comp, boolean launched) { - if (launched) { - mLaunchCount++; - } - mResumedTime = SystemClock.elapsedRealtime(); - } - - void updatePause() { - mPausedTime = SystemClock.elapsedRealtime(); - mUsageTime += (mPausedTime - mResumedTime); - } - void addLaunchCount(String comp) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -247,9 +250,7 @@ public final class UsageStatsService extends IUsageStats.Stub { times.add(millis); } - void writeToParcel(Parcel out) { - out.writeInt(mLaunchCount); - out.writeLong(mUsageTime); + public void writeExtendedToParcel(Parcel out, int parcelableFlags) { final int numLaunchTimeStats = mLaunchTimes.size(); out.writeInt(numLaunchTimeStats); for (int i=0; i<numLaunchTimeStats; i++) { @@ -264,11 +265,21 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void clear() { + @Override + public boolean clearUsageTimes() { mLaunchTimes.clear(); mFullyDrawnTimes.clear(); - mLaunchCount = 0; - mUsageTime = 0; + return super.clearUsageTimes(); + } + } + + static class ConfigUsageStatsExtended extends UsageStats.ConfigurationStats { + ConfigUsageStatsExtended(Configuration config) { + super(config); + } + + ConfigUsageStatsExtended(Parcel in) { + super(in); } } @@ -364,18 +375,9 @@ public final class UsageStatsService extends IUsageStats.Stub { + VERSION + "; dropping"); return; } - int N = in.readInt(); - while (N > 0) { - N--; - String pkgName = in.readString(); - if (pkgName == null) { - break; - } - if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName); - PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in); - synchronized (mStatsLock) { - mStats.put(pkgName, pus); - } + LocalUsageStats stats = new LocalUsageStats(in, true); + synchronized (mStatsLock) { + mStats = stats; } } @@ -419,12 +421,9 @@ public final class UsageStatsService extends IUsageStats.Stub { try { long lastResumeTime = Long.parseLong(lastResumeTimeStr); synchronized (mStatsLock) { - ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg); - if (lrt == null) { - lrt = new ArrayMap<String, Long>(); - mLastResumeTimes.put(pkg, lrt); - } - lrt.put(comp, lastResumeTime); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended) + mStats.getOrCreatePackageStats(pkg); + pus.componentResumeTimes.put(comp, lastResumeTime); } } catch (NumberFormatException e) { } @@ -543,6 +542,15 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } + Parcel out = Parcel.obtain(); + synchronized (mStatsLock) { + out.writeInt(VERSION); + mStats.writeExtendedToParcel(out, 0); + if (dayChanged) { + mStats.clearUsageTimes(); + } + } + synchronized (mFileLock) { // Get the most recent file mFileLeaf = getCurrentDateStr(FILE_PREFIX); @@ -553,6 +561,7 @@ public final class UsageStatsService extends IUsageStats.Stub { if (!backupFile.exists()) { if (!mFile.renameTo(backupFile)) { Slog.w(TAG, "Failed to persist new stats"); + out.recycle(); return; } } else { @@ -562,14 +571,10 @@ public final class UsageStatsService extends IUsageStats.Stub { try { // Write mStats to file - writeStatsFLOCK(mFile); + writeStatsFLOCK(mFile, out); mLastWriteElapsedTime.set(currElapsedTime); if (dayChanged) { mLastWriteDay.set(curDay); - // clear stats - synchronized (mStats) { - mStats.clear(); - } mFile = new File(mDir, mFileLeaf); checkFileLimitFLOCK(); } @@ -590,17 +595,15 @@ public final class UsageStatsService extends IUsageStats.Stub { backupFile.renameTo(mFile); } } + out.recycle(); } if (localLOGV) Slog.d(TAG, "Dumped usage stats."); } - private void writeStatsFLOCK(File file) throws IOException { + private void writeStatsFLOCK(File file, Parcel parcel) throws IOException { FileOutputStream stream = new FileOutputStream(file); try { - Parcel out = Parcel.obtain(); - writeStatsToParcelFLOCK(out); - stream.write(out.marshall()); - out.recycle(); + stream.write(parcel.marshall()); stream.flush(); } finally { FileUtils.sync(stream); @@ -608,29 +611,14 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - private void writeStatsToParcelFLOCK(Parcel out) { - synchronized (mStatsLock) { - out.writeInt(VERSION); - Set<String> keys = mStats.keySet(); - out.writeInt(keys.size()); - for (String key : keys) { - PkgUsageStatsExtended pus = mStats.get(key); - out.writeString(key); - pus.writeToParcel(out); - } - } - } - /** Filter out stats for any packages which aren't present anymore. */ private void filterHistoryStats() { synchronized (mStatsLock) { IPackageManager pm = AppGlobals.getPackageManager(); - for (int i=0; i<mLastResumeTimes.size(); i++) { - String pkg = mLastResumeTimes.keyAt(i); + for (int i=mStats.mPackages.size()-1; i>=0; i--) { try { - if (pm.getPackageUid(pkg, 0) < 0) { - mLastResumeTimes.removeAt(i); - i--; + if (pm.getPackageUid(mStats.mPackages.valueAt(i).getPackageName(), 0) < 0) { + mStats.mPackages.removeAt(i); } } catch (RemoteException e) { } @@ -648,10 +636,12 @@ public final class UsageStatsService extends IUsageStats.Stub { out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "usage-history"); synchronized (mStatsLock) { - for (int i=0; i<mLastResumeTimes.size(); i++) { + int NP = mStats.mPackages.size(); + for (int i=0; i<NP; i++) { + UsageStats.PackageStats ps = mStats.mPackages.valueAt(i); out.startTag(null, "pkg"); - out.attribute(null, "name", mLastResumeTimes.keyAt(i)); - ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i); + out.attribute(null, "name", ps.getPackageName()); + ArrayMap<String, Long> comp = ps.componentResumeTimes; for (int j=0; j<comp.size(); j++) { out.startTag(null, "comp"); out.attribute(null, "name", comp.keyAt(j)); @@ -678,6 +668,10 @@ public final class UsageStatsService extends IUsageStats.Stub { ServiceManager.addService(SERVICE_NAME, asBinder()); } + public void systemReady() { + mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); + } + /** * Start watching packages to remove stats when a package is uninstalled. * May only be called when the package manager is ready. @@ -687,7 +681,7 @@ public final class UsageStatsService extends IUsageStats.Stub { @Override public void onPackageRemovedAllUsers(String packageName, int uid) { synchronized (mStatsLock) { - mLastResumeTimes.remove(packageName); + mStats.mPackages.remove(packageName); } } }; @@ -729,9 +723,10 @@ public final class UsageStatsService extends IUsageStats.Stub { // to recover. if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName + " while already resumed in " + mLastResumedPkg); - PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats( + mLastResumedPkg); if (pus != null) { - pus.updatePause(); + pus.pause(); } } } @@ -744,22 +739,13 @@ public final class UsageStatsService extends IUsageStats.Stub { mLastResumedComp = componentName.getClassName(); if (localLOGV) Slog.i(TAG, "started component:" + pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - pus = new PkgUsageStatsExtended(); - mStats.put(pkgName, pus); - } - pus.updateResume(mLastResumedComp, !samePackage); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended) + mStats.getOrCreatePackageStats(pkgName); + pus.resume(!samePackage); if (!sameComp) { pus.addLaunchCount(mLastResumedComp); } - - ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName); - if (componentResumeTimes == null) { - componentResumeTimes = new ArrayMap<String, Long>(); - mLastResumeTimes.put(pkgName, componentResumeTimes); - } - componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis()); + pus.componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis()); } } @@ -782,13 +768,13 @@ public final class UsageStatsService extends IUsageStats.Stub { if (localLOGV) Slog.i(TAG, "paused component:"+pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus == null) { // Weird some error here Slog.i(TAG, "No package stats for pkg:"+pkgName); return; } - pus.updatePause(); + pus.pause(); } // Persist current data to file if needed. @@ -808,7 +794,7 @@ public final class UsageStatsService extends IUsageStats.Stub { writeStatsToFile(false, false); synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus != null) { pus.addLaunchTime(componentName.getClassName(), millis); } @@ -827,13 +813,29 @@ public final class UsageStatsService extends IUsageStats.Stub { writeStatsToFile(false, false); synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); if (pus != null) { pus.addFullyDrawnTime(componentName.getClassName(), millis); } } } + public void noteStartConfig(Configuration config) { + enforceCallingPermission(); + synchronized (mStatsLock) { + config = new Configuration(config); + ConfigUsageStatsExtended cus = (ConfigUsageStatsExtended) + mStats.getOrCreateConfigurationStats(config); + if (cus != mCurrentConfigStats) { + if (mCurrentConfigStats != null) { + mCurrentConfigStats.stop(); + } + cus.start(); + mCurrentConfigStats = cus; + } + } + } + public void enforceCallingPermission() { if (Binder.getCallingPid() == Process.myPid()) { return; @@ -843,53 +845,71 @@ public final class UsageStatsService extends IUsageStats.Stub { } @Override - public PkgUsageStats getPkgUsageStats(ComponentName componentName) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); + public UsageStats.PackageStats getPkgUsageStats(String callingPkg, + ComponentName componentName) { + checkCallerPermission(callingPkg, "getPkgUsageStats"); String pkgName; if ((componentName == null) || ((pkgName = componentName.getPackageName()) == null)) { return null; } synchronized (mStatsLock) { - PkgUsageStatsExtended pus = mStats.get(pkgName); - Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName); - if (pus == null && lastResumeTimes == null) { + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName); + if (pus == null) { return null; } - int launchCount = pus != null ? pus.mLaunchCount : 0; - long usageTime = pus != null ? pus.mUsageTime : 0; - return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes); + return new UsageStats.PackageStats(pus); } } @Override - public PkgUsageStats[] getAllPkgUsageStats() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.PACKAGE_USAGE_STATS, null); + public UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg) { + checkCallerPermission(callingPkg, "getAllPkgUsageStats"); synchronized (mStatsLock) { - int size = mLastResumeTimes.size(); - if (size <= 0) { + int NP = mStats.mPackages.size(); + if (NP <= 0) { return null; } - PkgUsageStats retArr[] = new PkgUsageStats[size]; - for (int i=0; i<size; i++) { - String pkg = mLastResumeTimes.keyAt(i); - long usageTime = 0; - int launchCount = 0; - - PkgUsageStatsExtended pus = mStats.get(pkg); - if (pus != null) { - usageTime = pus.mUsageTime; - launchCount = pus.mLaunchCount; - } - retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime, - mLastResumeTimes.valueAt(i)); + UsageStats.PackageStats retArr[] = new UsageStats.PackageStats[NP]; + for (int p=0; p<NP; p++) { + UsageStats.PackageStats ps = mStats.mPackages.valueAt(p); + retArr[p] = new UsageStats.PackageStats(ps); } return retArr; } } + @Override + public ParcelableParcel getCurrentStats(String callingPkg) { + checkCallerPermission(callingPkg, "getCurrentStats"); + synchronized (mStatsLock) { + ParcelableParcel out = new ParcelableParcel(null); + mStats.writeToParcel(out.getParcel(), 0); + return out; + } + } + + private void checkCallerPermission(String callingPkg, String callingOp) { + // Because the permission for this is system-only, its use with + // app ops is a little different: the op is disabled by default, + // and enabling it allows apps to get access even if they don't + // hold the permission. + int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, Binder.getCallingUid(), + callingPkg); + if (mode == AppOpsManager.MODE_ALLOWED) { + return; + } else if (mode != AppOpsManager.MODE_IGNORED) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_USAGE_STATS) + == PackageManager.PERMISSION_GRANTED) { + return; + } + } + + String msg = "Package " + callingPkg + " not allowed to call " + callingOp; + throw new SecurityException(msg); + } + static byte[] readFully(FileInputStream stream) throws IOException { int pos = 0; int avail = stream.available(); @@ -963,31 +983,28 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } - pw.println(sb.toString()); - int N = in.readInt(); + final LocalUsageStats stats = new LocalUsageStats(in, true); + final long time = SystemClock.elapsedRealtime(); - while (N > 0) { - N--; - String pkgName = in.readString(); - if (pkgName == null) { - break; - } + pw.println(sb.toString()); + int NP = stats.mPackages.size(); + for (int p=0; p<NP; p++) { + PkgUsageStatsExtended pus = (PkgUsageStatsExtended)stats.mPackages.valueAt(p); sb.setLength(0); - PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in); - if (packages != null && !packages.contains(pkgName)) { + if (packages != null && !packages.contains(pus.getPackageName())) { // This package has not been requested -- don't print // anything for it. } else if (isCompactOutput) { sb.append("P:"); - sb.append(pkgName); + sb.append(pus.getPackageName()); sb.append(','); - sb.append(pus.mLaunchCount); + sb.append(pus.getLaunchCount()); sb.append(','); - sb.append(pus.mUsageTime); + sb.append(pus.getUsageTime(time)); sb.append('\n'); final int NLT = pus.mLaunchTimes.size(); for (int i=0; i<NLT; i++) { - sb.append("A:"); + sb.append("L:"); String activity = pus.mLaunchTimes.keyAt(i); sb.append(activity); TimeStats times = pus.mLaunchTimes.valueAt(i); @@ -1001,7 +1018,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } final int NFDT = pus.mFullyDrawnTimes.size(); for (int i=0; i<NFDT; i++) { - sb.append("A:"); + sb.append("D:"); String activity = pus.mFullyDrawnTimes.keyAt(i); sb.append(activity); TimeStats times = pus.mFullyDrawnTimes.valueAt(i); @@ -1011,15 +1028,23 @@ public final class UsageStatsService extends IUsageStats.Stub { } sb.append('\n'); } + final int NC = pus.componentResumeTimes.size(); + for (int c=0; c<NC; c++) { + pw.print("R:"); pw.print(pus.componentResumeTimes.keyAt(c)); pw.print(","); + pw.println(pus.componentResumeTimes.valueAt(c)); + } } else { sb.append(" "); - sb.append(pkgName); - sb.append(": "); - sb.append(pus.mLaunchCount); - sb.append(" times, "); - sb.append(pus.mUsageTime); - sb.append(" ms"); + sb.append(pus.getPackageName()); + if (pus.getLaunchCount() != 0 || pus.getUsageTime(time) != 0) { + sb.append(": "); + sb.append(pus.getLaunchCount()); + sb.append(" times, "); + TimeUtils.formatDuration(pus.getUsageTime(time), sb); + } else { + sb.append(":"); + } sb.append('\n'); final int NLT = pus.mLaunchTimes.size(); for (int i=0; i<NLT; i++) { @@ -1084,10 +1109,50 @@ public final class UsageStatsService extends IUsageStats.Stub { } sb.append('\n'); } + final int NC = pus.componentResumeTimes.size(); + for (int c=0; c<NC; c++) { + sb.append(" "); + sb.append(pus.componentResumeTimes.keyAt(c)); + sb.append(" last resumed "); + sb.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", + pus.componentResumeTimes.valueAt(c)).toString()); + sb.append('\n'); + } } pw.write(sb.toString()); } + if (packages == null) { + int NC = stats.mConfigurations.size(); + for (int c=0; c<NC; c++) { + ConfigUsageStatsExtended cus + = (ConfigUsageStatsExtended)stats.mConfigurations.valueAt(c); + sb.setLength(0); + if (isCompactOutput) { + sb.append("C:"); sb.append(cus.getConfiguration().toString()); + sb.append(","); sb.append(cus.getUsageCount()); sb.append(","); + sb.append(cus.getUsageTime(time)); + } else { + sb.append(" "); + sb.append(cus.getConfiguration().toString()); + sb.append(":\n"); + if (cus.getUsageCount() != 0 || cus.getUsageTime(time) != 0) { + sb.append(" Used "); + sb.append(cus.getUsageCount()); + sb.append(" times, "); + TimeUtils.formatDuration(cus.getUsageTime(time), sb); + sb.append("\n"); + } + if (cus.getLastUsedTime() > 0) { + sb.append(" Last used: "); + sb.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss", + cus.getLastUsedTime()).toString()); + sb.append("\n"); + } + } + pw.write(sb.toString()); + } + } } /** @@ -1174,5 +1239,4 @@ public final class UsageStatsService extends IUsageStats.Stub { collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages); } } - } diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 8815d0f..0749f24 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -24,7 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; -import android.net.ProxyProperties; +import android.net.ProxyInfo; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -157,14 +157,14 @@ public class PacManager { * @param proxy Proxy information that is about to be broadcast. * @return Returns true when the broadcast should not be sent */ - public synchronized boolean setCurrentProxyScriptUrl(ProxyProperties proxy) { - if (!TextUtils.isEmpty(proxy.getPacFileUrl())) { + public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) { + if (proxy.getPacFileUrl() != null) { if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) { // Allow to send broadcast, nothing to do. return false; } synchronized (mProxyLock) { - mPacUrl = proxy.getPacFileUrl(); + mPacUrl = proxy.getPacFileUrl().toString(); } mCurrentDelay = DELAY_1; mHasSentBroadcast = false; @@ -268,7 +268,7 @@ public class PacManager { // Already bound no need to bind again. if ((mProxyConnection != null) && (mConnection != null)) { if (mLastPort != -1) { - sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort)); + sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort)); } else { Log.e(TAG, "Received invalid port from Local Proxy," + " PAC will not be operational"); @@ -362,7 +362,7 @@ public class PacManager { mLastPort = -1; } - private void sendPacBroadcast(ProxyProperties proxy) { + private void sendPacBroadcast(ProxyInfo proxy) { mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy)); } @@ -371,7 +371,7 @@ public class PacManager { return; } if (!mHasSentBroadcast) { - sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort)); + sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort)); mHasSentBroadcast = true; } } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index fd2f8a1..8968da3 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -642,8 +642,9 @@ public final class DreamManagerService extends SystemService { try { synchronized (mMcuHal) { if (mReleased) { - throw new IllegalStateException("This operation cannot be performed " - + "because the dream has ended."); + Slog.w(TAG, "Ignoring message to MCU HAL because the dream " + + "has already ended: " + msg); + return null; } return mMcuHal.sendMessage(msg, arg); } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 3dc17fc..015032b 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -17,6 +17,7 @@ package com.android.server.media; import android.content.Intent; +import android.content.pm.PackageManager; import android.media.routeprovider.RouteRequest; import android.media.session.ISessionController; import android.media.session.ISessionControllerCallback; @@ -59,6 +60,24 @@ import java.util.UUID; public class MediaSessionRecord implements IBinder.DeathRecipient { private static final String TAG = "MediaSessionRecord"; + /** + * These are the playback states that count as currently active. + */ + private static final int[] ACTIVE_STATES = { + PlaybackState.PLAYSTATE_FAST_FORWARDING, + PlaybackState.PLAYSTATE_REWINDING, + PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS, + PlaybackState.PLAYSTATE_SKIPPING_FORWARDS, + PlaybackState.PLAYSTATE_BUFFERING, + PlaybackState.PLAYSTATE_CONNECTING, + PlaybackState.PLAYSTATE_PLAYING }; + + /** + * The length of time a session will still be considered active after + * pausing in ms. + */ + private static final int ACTIVE_BUFFER = 30000; + private final MessageHandler mHandler; private final int mPid; @@ -74,21 +93,22 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { new ArrayList<ISessionControllerCallback>(); private final ArrayList<RouteRequest> mRequests = new ArrayList<RouteRequest>(); - private boolean mTransportPerformerEnabled = false; private RouteInfo mRoute; private RouteOptions mRequest; private RouteConnectionRecord mConnection; // TODO define a RouteState class with relevant info private int mRouteState; + private long mFlags; // TransportPerformer fields private MediaMetadata mMetadata; private PlaybackState mPlaybackState; private int mRatingType; + private long mLastActiveTime; // End TransportPerformer fields - private boolean mIsPublished = false; + private boolean mIsActive = false; public MediaSessionRecord(int pid, String packageName, ISessionCallback cb, String tag, MediaSessionService service, Handler handler) { @@ -148,6 +168,35 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } /** + * Get this session's flags. + * + * @return The flags for this session. + */ + public long getFlags() { + return mFlags; + } + + /** + * Check if this session has the specified flag. + * + * @param flag The flag to check. + * @return True if this session has that flag set, false otherwise. + */ + public boolean hasFlag(int flag) { + return (mFlags & flag) != 0; + } + + /** + * Check if this session has system priorty and should receive media buttons + * before any other sessions. + * + * @return True if this is a system priority session, false otherwise + */ + public boolean isSystemPriority() { + return (mFlags & Session.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0; + } + + /** * Set the selected route. This does not connect to the route, just notifies * the app that a new route has been selected. * @@ -215,12 +264,36 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } /** - * Check if this session has been published by the app yet. + * Check if this session has been set to active by the app. + * + * @return True if the session is active, false otherwise. + */ + public boolean isActive() { + return mIsActive; + } + + /** + * Check if the session is currently performing playback. This will also + * return true if the session was recently paused. * - * @return True if it has been published, false otherwise. + * @return True if the session is performing playback, false otherwise. */ - public boolean isPublished() { - return mIsPublished; + public boolean isPlaybackActive() { + int state = mPlaybackState == null ? 0 : mPlaybackState.getState(); + if (isActiveState(state)) { + return true; + } + if (state == mPlaybackState.PLAYSTATE_PAUSED) { + long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime; + if (inactiveTime < ACTIVE_BUFFER) { + return true; + } + } + return false; + } + + public boolean isTransportControlEnabled() { + return hasFlag(Session.FLAG_HANDLES_TRANSPORT_CONTROLS); } @Override @@ -234,11 +307,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { final String indent = prefix + " "; pw.println(indent + "pid=" + mPid); pw.println(indent + "info=" + mSessionInfo.toString()); - pw.println(indent + "published=" + mIsPublished); - pw.println(indent + "transport controls enabled=" + mTransportPerformerEnabled); + pw.println(indent + "published=" + mIsActive); + pw.println(indent + "flags=" + mFlags); pw.println(indent + "rating type=" + mRatingType); pw.println(indent + "controllers: " + mControllerCallbacks.size()); - pw.println(indent + "state=" + mPlaybackState.toString()); + pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString())); pw.println(indent + "metadata:" + getShortMetadataString()); pw.println(indent + "route requests {"); int size = mRequests.size(); @@ -251,6 +324,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { pw.println(indent + "params=" + (mRequest == null ? null : mRequest.toString())); } + private boolean isActiveState(int state) { + for (int i = 0; i < ACTIVE_STATES.length; i++) { + if (ACTIVE_STATES[i] == state) { + return true; + } + } + return false; + } + private String getShortMetadataString() { int fields = mMetadata == null ? 0 : mMetadata.size(); String title = mMetadata == null ? null : mMetadata @@ -393,12 +475,21 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override - public void publish() { - mIsPublished = true; // TODO push update to service + public void setActive(boolean active) { + mIsActive = active; + mService.updateSession(MediaSessionRecord.this); + mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); } + @Override - public void setTransportPerformerEnabled() { - mTransportPerformerEnabled = true; + public void setFlags(int flags) { + if ((flags & Session.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { + int pid = getCallingPid(); + int uid = getCallingUid(); + mService.enforcePhoneStatePermission(pid, uid); + } + mFlags = flags; + mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE); } @Override @@ -409,7 +500,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public void setPlaybackState(PlaybackState state) { + int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState(); + int newState = state == null ? 0 : state.getState(); + if (isActiveState(oldState) && newState == PlaybackState.PLAYSTATE_PAUSED) { + mLastActiveTime = SystemClock.elapsedRealtime(); + } mPlaybackState = state; + mService.onSessionPlaystateChange(MediaSessionRecord.this, oldState, newState); mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE); } @@ -673,7 +770,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { @Override public boolean isTransportControlEnabled() { - return mTransportPerformerEnabled; + return MediaSessionRecord.this.isTransportControlEnabled(); } @Override @@ -689,6 +786,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private static final int MSG_SEND_EVENT = 4; private static final int MSG_UPDATE_ROUTE_FILTERS = 5; private static final int MSG_SEND_COMMAND = 6; + private static final int MSG_UPDATE_SESSION_STATE = 7; public MessageHandler(Looper looper) { super(looper); @@ -713,6 +811,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { (Pair<RouteCommand, ResultReceiver>) msg.obj; pushRouteCommand(cmd.first, cmd.second); break; + case MSG_UPDATE_SESSION_STATE: + // TODO add session state + break; } } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 107f6ad..fb858fc 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -17,17 +17,25 @@ package com.android.server.media; import android.Manifest; +import android.app.ActivityManager; +import android.content.ComponentName; import android.content.Context; 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.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; @@ -38,6 +46,7 @@ import com.android.server.Watchdog.Monitor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; /** * System implementation of MediaSessionManager @@ -48,15 +57,17 @@ public class MediaSessionService extends SystemService implements Monitor { private final SessionManagerImpl mSessionManagerImpl; private final MediaRouteProviderWatcher mRouteProviderWatcher; + private final MediaSessionStack mPriorityStack; - private final ArrayList<MediaSessionRecord> mSessions - = new ArrayList<MediaSessionRecord>(); + private final ArrayList<MediaSessionRecord> mRecords = new ArrayList<MediaSessionRecord>(); private final ArrayList<MediaRouteProviderProxy> mProviders = new ArrayList<MediaRouteProviderProxy>(); private final Object mLock = new Object(); // TODO do we want a separate thread for handling mediasession messages? private final Handler mHandler = new Handler(); + private MediaSessionRecord mPrioritySession; + // Used to keep track of the current request to show routes for a specific // session so we drop late callbacks properly. private int mShowRoutesRequestId = 0; @@ -69,6 +80,7 @@ public class MediaSessionService extends SystemService implements Monitor { mSessionManagerImpl = new SessionManagerImpl(); mRouteProviderWatcher = new MediaRouteProviderWatcher(context, mProviderWatcherCallback, mHandler, context.getUserId()); + mPriorityStack = new MediaSessionStack(); } @Override @@ -121,6 +133,30 @@ public class MediaSessionService extends SystemService implements Monitor { } } + public void updateSession(MediaSessionRecord record) { + synchronized (mLock) { + mPriorityStack.onSessionStateChange(record); + if (record.isSystemPriority()) { + if (record.isActive()) { + if (mPrioritySession != null) { + Log.w(TAG, "Replacing existing priority session with a new session"); + } + mPrioritySession = record; + } else { + if (mPrioritySession == record) { + mPrioritySession = null; + } + } + } + } + } + + public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { + synchronized (mLock) { + mPriorityStack.onPlaystateChange(record, oldState, newState); + } + } + @Override public void monitor() { synchronized (mLock) { @@ -141,7 +177,11 @@ public class MediaSessionService extends SystemService implements Monitor { } private void destroySessionLocked(MediaSessionRecord session) { - mSessions.remove(session); + mRecords.remove(session); + mPriorityStack.removeSession(session); + if (session == mPrioritySession) { + mPrioritySession = null; + } } private void enforcePackageName(String packageName, int uid) { @@ -158,8 +198,64 @@ 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: + * <ul> + * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL + * permission</li> + * <li>the caller's listener is one of the enabled notification listeners</li> + * </ul> + */ + private void enforceMediaPermissions(ComponentName compName, int pid, int uid) { + if (getContext() + .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) + != PackageManager.PERMISSION_GRANTED + && !isEnabledNotificationListener(compName)) { + throw new SecurityException("Missing permission to control media."); + } + } + + private boolean isEnabledNotificationListener(ComponentName compName) { + if (compName != null) { + final int currentUser = ActivityManager.getCurrentUser(); + final String enabledNotifListeners = Settings.Secure.getStringForUser( + getContext().getContentResolver(), + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, + currentUser); + if (enabledNotifListeners != null) { + final String[] components = enabledNotifListeners.split(":"); + for (int i = 0; i < components.length; i++) { + final ComponentName component = + ComponentName.unflattenFromString(components[i]); + if (component != null) { + if (compName.equals(component)) { + if (DEBUG) { + Log.d(TAG, "ok to get sessions: " + component + + " is authorized notification listener"); + } + return true; + } + } + } + } + if (DEBUG) { + Log.d(TAG, "not ok to get sessions, " + compName + + " is not in list of ENABLED_NOTIFICATION_LISTENERS"); + } + } + return false; + } + private MediaSessionRecord createSessionInternal(int pid, String packageName, - ISessionCallback cb, String tag) { + ISessionCallback cb, String tag, boolean forCalls) { synchronized (mLock) { return createSessionLocked(pid, packageName, cb, tag); } @@ -174,13 +270,24 @@ public class MediaSessionService extends SystemService implements Monitor { } catch (RemoteException e) { throw new RuntimeException("Media Session owner died prematurely.", e); } - mSessions.add(session); + mRecords.add(session); + mPriorityStack.addSession(session); if (DEBUG) { Log.d(TAG, "Created session for package " + packageName + " with tag " + tag); } return session; } + private int findIndexOfSessionForIdLocked(String sessionId) { + for (int i = mRecords.size() - 1; i >= 0; i--) { + MediaSessionRecord session = mRecords.get(i); + if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) { + return i; + } + } + return -1; + } + private MediaRouteProviderProxy getProviderLocked(String providerId) { for (int i = mProviders.size() - 1; i >= 0; i--) { MediaRouteProviderProxy provider = mProviders.get(i); @@ -191,14 +298,9 @@ public class MediaSessionService extends SystemService implements Monitor { return null; } - private int findIndexOfSessionForIdLocked(String sessionId) { - for (int i = mSessions.size() - 1; i >= 0; i--) { - MediaSessionRecord session = mSessions.get(i); - if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) { - return i; - } - } - return -1; + private boolean isSessionDiscoverable(MediaSessionRecord record) { + // TODO probably want to check more than if it's published. + return record.isActive(); } private MediaRouteProviderWatcher.Callback mProviderWatcherCallback @@ -232,7 +334,7 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int index = findIndexOfSessionForIdLocked(sessionId); if (index != -1 && routes != null && routes.size() > 0) { - MediaSessionRecord record = mSessions.get(index); + MediaSessionRecord record = mRecords.get(index); record.selectRoute(routes.get(0)); } } @@ -244,7 +346,7 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int index = findIndexOfSessionForIdLocked(sessionId); if (index != -1) { - MediaSessionRecord session = mSessions.get(index); + MediaSessionRecord session = mRecords.get(index); session.setRouteConnected(route, options.getConnectionOptions(), connection); } } @@ -266,7 +368,37 @@ public class MediaSessionService extends SystemService implements Monitor { if (cb == null) { throw new IllegalArgumentException("Controller callback cannot be null"); } - return createSessionInternal(pid, packageName, cb, tag).getSessionBinder(); + return createSessionInternal(pid, packageName, cb, tag, false).getSessionBinder(); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public List<IBinder> getSessions(ComponentName componentName) { + final int pid = Binder.getCallingPid(); + final int uid = Binder.getCallingUid(); + final long token = Binder.clearCallingIdentity(); + + try { + if (componentName != null) { + // If they gave us a component name verify they own the + // package + enforcePackageName(componentName.getPackageName(), uid); + } + // Then check if they have the permissions or their component is + // allowed + enforceMediaPermissions(componentName, pid, uid); + ArrayList<IBinder> binders = new ArrayList<IBinder>(); + synchronized (mLock) { + ArrayList<MediaSessionRecord> records = mPriorityStack + .getActiveSessions(); + int size = records.size(); + for (int i = 0; i < size; i++) { + binders.add(records.get(i).getControllerBinder().asBinder()); + } + } + return binders; } finally { Binder.restoreCallingIdentity(token); } @@ -286,13 +418,18 @@ public class MediaSessionService extends SystemService implements Monitor { pw.println(); synchronized (mLock) { - int count = mSessions.size(); - pw.println("Sessions - have " + count + " states:"); + pw.println("Session for calls:" + mPrioritySession); + if (mPrioritySession != null) { + mPrioritySession.dump(pw, ""); + } + int count = mRecords.size(); + pw.println(count + " Sessions:"); for (int i = 0; i < count; i++) { - MediaSessionRecord record = mSessions.get(i); + mRecords.get(i).dump(pw, ""); pw.println(); - record.dump(pw, ""); } + mPriorityStack.dumpLocked(pw, ""); + pw.println("Providers:"); count = mProviders.size(); for (int i = 0; i < count; i++) { diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java new file mode 100644 index 0000000..f9f004d --- /dev/null +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -0,0 +1,267 @@ +/* + * 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.media; + +import android.media.session.PlaybackState; +import android.media.session.Session; +import android.text.TextUtils; + +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Keeps track of media sessions and their priority for notifications, media + * button routing, etc. + */ +public class MediaSessionStack { + /** + * These are states that usually indicate the user took an action and should + * bump priority regardless of the old state. + */ + private static final int[] ALWAYS_PRIORITY_STATES = { + PlaybackState.PLAYSTATE_FAST_FORWARDING, + PlaybackState.PLAYSTATE_REWINDING, + PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS, + PlaybackState.PLAYSTATE_SKIPPING_FORWARDS }; + /** + * These are states that usually indicate the user took an action if they + * were entered from a non-priority state. + */ + private static final int[] TRANSITION_PRIORITY_STATES = { + PlaybackState.PLAYSTATE_BUFFERING, + PlaybackState.PLAYSTATE_CONNECTING, + PlaybackState.PLAYSTATE_PLAYING }; + + private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); + + private MediaSessionRecord mCachedButtonReceiver; + private MediaSessionRecord mCachedDefault; + private ArrayList<MediaSessionRecord> mCachedActiveList; + private ArrayList<MediaSessionRecord> mCachedTransportControlList; + + /** + * Add a record to the priority tracker. + * + * @param record The record to add. + */ + public void addSession(MediaSessionRecord record) { + mSessions.add(record); + clearCache(); + } + + /** + * Remove a record from the priority tracker. + * + * @param record The record to remove. + */ + public void removeSession(MediaSessionRecord record) { + mSessions.remove(record); + clearCache(); + } + + /** + * Notify the priority tracker that a session's state changed. + * + * @param record The record that changed. + * @param oldState Its old playback state. + * @param newState Its new playback state. + */ + public void onPlaystateChange(MediaSessionRecord record, int oldState, int newState) { + if (shouldUpdatePriority(oldState, newState)) { + mSessions.remove(record); + mSessions.add(0, record); + clearCache(); + } + } + + /** + * Handle any stack changes that need to occur in response to a session + * state change. TODO add the old and new session state as params + * + * @param record The record that changed. + */ + public void onSessionStateChange(MediaSessionRecord record) { + // For now just clear the cache. Eventually we'll selectively clear + // depending on what changed. + clearCache(); + } + + /** + * Get the current priority sorted list of active sessions. The most + * important session is at index 0 and the least important at size - 1. + * + * @return All the active sessions in priority order. + */ + public ArrayList<MediaSessionRecord> getActiveSessions() { + if (mCachedActiveList == null) { + mCachedActiveList = getPriorityListLocked(true, 0); + } + return mCachedActiveList; + } + + /** + * Get the current priority sorted list of active sessions that use + * transport controls. The most important session is at index 0 and the + * least important at size -1. + * + * @return All the active sessions that handle transport controls in + * priority order. + */ + public ArrayList<MediaSessionRecord> getTransportControlSessions() { + if (mCachedTransportControlList == null) { + mCachedTransportControlList = getPriorityListLocked(true, + Session.FLAG_HANDLES_TRANSPORT_CONTROLS); + } + return mCachedTransportControlList; + } + + /** + * Get the highest priority active session. + * + * @return The current highest priority session or null. + */ + public MediaSessionRecord getDefaultSession() { + if (mCachedDefault != null) { + return mCachedDefault; + } + ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0); + if (records.size() > 0) { + return records.get(0); + } + return null; + } + + /** + * Get the highest priority session that can handle media buttons. + * + * @return The default media button session or null. + */ + public MediaSessionRecord getDefaultMediaButtonSession() { + if (mCachedButtonReceiver != null) { + return mCachedButtonReceiver; + } + ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, + Session.FLAG_HANDLES_MEDIA_BUTTONS); + if (records.size() > 0) { + mCachedButtonReceiver = records.get(0); + } + return mCachedButtonReceiver; + } + + public void dumpLocked(PrintWriter pw, String prefix) { + ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0); + int count = sortedSessions.size(); + pw.println(prefix + "Sessions Stack - have " + count + " sessions:"); + String indent = prefix + " "; + for (int i = 0; i < count; i++) { + MediaSessionRecord record = sortedSessions.get(i); + record.dump(pw, indent); + pw.println(); + } + } + + /** + * Get a priority sorted list of sessions. Can filter to only return active + * sessions or sessions with specific flags. + * + * @param activeOnly True to only return active sessions, false to return + * all sessions. + * @param withFlags Only return sessions with all the specified flags set. 0 + * returns all sessions. + * @return The priority sorted list of sessions. + */ + private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags) { + ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); + int lastLocalIndex = 0; + int lastActiveIndex = 0; + int lastPublishedIndex = 0; + + int size = mSessions.size(); + for (int i = 0; i < size; i++) { + final MediaSessionRecord session = mSessions.get(i); + + if ((session.getFlags() & withFlags) != withFlags) { + continue; + } + if (!session.isActive()) { + if (!activeOnly) { + // If we're getting unpublished as well always put them at + // the end + result.add(session); + } + continue; + } + + if (session.isSystemPriority()) { + // System priority sessions are special and always go at the + // front. We expect there to only be one of these at a time. + result.add(0, session); + lastLocalIndex++; + lastActiveIndex++; + lastPublishedIndex++; + } else if (session.isPlaybackActive()) { + // TODO replace getRoute() == null with real local route check + if(session.getRoute() == null) { + // Active local sessions get top priority + result.add(lastLocalIndex, session); + lastLocalIndex++; + lastActiveIndex++; + lastPublishedIndex++; + } else { + // Then active remote sessions + result.add(lastActiveIndex, session); + lastActiveIndex++; + lastPublishedIndex++; + } + } else { + // inactive sessions go at the end in order of whoever last did + // something. + result.add(lastPublishedIndex, session); + lastPublishedIndex++; + } + } + + return result; + } + + private boolean shouldUpdatePriority(int oldState, int newState) { + if (containsState(newState, ALWAYS_PRIORITY_STATES)) { + return true; + } + if (!containsState(oldState, TRANSITION_PRIORITY_STATES) + && containsState(newState, TRANSITION_PRIORITY_STATES)) { + return true; + } + return false; + } + + private boolean containsState(int state, int[] states) { + for (int i = 0; i < states.length; i++) { + if (states[i] == state) { + return true; + } + } + return false; + } + + private void clearCache() { + mCachedDefault = null; + mCachedButtonReceiver = null; + mCachedActiveList = null; + mCachedTransportControlList = null; + } +} diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7aa5d79..fce86e8 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -198,8 +198,7 @@ public class NotificationManagerService extends SystemService { private final UserProfiles mUserProfiles = new UserProfiles(); private NotificationListeners mListeners; private ConditionProviders mConditionProviders; - - private final NotificationUsageStats mUsageStats = new NotificationUsageStats(); + private NotificationUsageStats mUsageStats; private static final String EXTRA_INTERCEPT = "android.intercept"; @@ -472,6 +471,7 @@ public class NotificationManagerService extends SystemService { pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x", notification.defaults, notification.flags)); pw.println(prefix + " sound=" + notification.sound); + pw.println(prefix + String.format(" color=0x%08x", notification.color)); pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate)); pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d", notification.ledARGB, notification.ledOnMS, notification.ledOffMS)); @@ -858,6 +858,7 @@ public class NotificationManagerService extends SystemService { }); final File systemDir = new File(Environment.getDataDirectory(), "system"); mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml")); + mUsageStats = new NotificationUsageStats(getContext()); importOldBlockDb(); diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index d9e2b91..45ab3d3 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -18,8 +18,17 @@ package com.android.server.notification; import com.android.server.notification.NotificationManagerService.NotificationRecord; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; import android.os.SystemClock; import android.service.notification.StatusBarNotification; +import android.util.Log; import java.io.PrintWriter; import java.util.HashMap; @@ -37,9 +46,13 @@ import java.util.Map; * {@hide} */ public class NotificationUsageStats { - // Guarded by synchronized(this). private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>(); + private final SQLiteLog mSQLiteLog; + + public NotificationUsageStats(Context context) { + mSQLiteLog = new SQLiteLog(context); + } /** * Called when a notification has been posted. @@ -49,6 +62,7 @@ public class NotificationUsageStats { for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.numPostedByApp++; } + mSQLiteLog.logPosted(notification); } /** @@ -68,6 +82,7 @@ public class NotificationUsageStats { stats.numRemovedByApp++; stats.collect(notification.stats); } + mSQLiteLog.logRemoved(notification); } /** @@ -79,6 +94,7 @@ public class NotificationUsageStats { stats.numDismissedByUser++; stats.collect(notification.stats); } + mSQLiteLog.logDismissed(notification); } /** @@ -89,6 +105,7 @@ public class NotificationUsageStats { for (AggregatedStats stats : getAggregatedStatsLocked(notification)) { stats.numClickedByUser++; } + mSQLiteLog.logClicked(notification); } /** @@ -146,6 +163,7 @@ public class NotificationUsageStats { for (AggregatedStats as : mStats.values()) { as.dump(pw, indent); } + mSQLiteLog.dump(pw, indent); } /** @@ -274,4 +292,211 @@ public class NotificationUsageStats { '}'; } } + + private static class SQLiteLog { + private static final String TAG = "NotificationSQLiteLog"; + + // Message types passed to the background handler. + private static final int MSG_POST = 1; + private static final int MSG_CLICK = 2; + private static final int MSG_REMOVE = 3; + private static final int MSG_DISMISS = 4; + + private static final String DB_NAME = "notification_log.db"; + private static final int DB_VERSION = 1; + + /** Age in ms after which events are pruned from the DB. */ + private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week + /** Delay between pruning the DB. Used to throttle pruning. */ + private static final long PRUNE_MIN_DELAY_MS = 6 * 60 * 60 * 1000L; // 6 hours + /** Mininum number of writes between pruning the DB. Used to throttle pruning. */ + private static final long PRUNE_MIN_WRITES = 1024; + + // Table 'log' + private static final String TAB_LOG = "log"; + private static final String COL_EVENT_USER_ID = "event_user_id"; + private static final String COL_EVENT_TYPE = "event_type"; + private static final String COL_EVENT_TIME = "event_time_ms"; + private static final String COL_KEY = "key"; + private static final String COL_PKG = "pkg"; + private static final String COL_NOTIFICATION_ID = "nid"; + private static final String COL_TAG = "tag"; + private static final String COL_WHEN_MS = "when_ms"; + private static final String COL_DEFAULTS = "defaults"; + private static final String COL_FLAGS = "flags"; + 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 int EVENT_TYPE_POST = 1; + private static final int EVENT_TYPE_CLICK = 2; + private static final int EVENT_TYPE_REMOVE = 3; + private static final int EVENT_TYPE_DISMISS = 4; + + private static long sLastPruneMs; + private static long sNumWrites; + + private final SQLiteOpenHelper mHelper; + private final Handler mWriteHandler; + + private static final long DAY_MS = 24 * 60 * 60 * 1000; + + public SQLiteLog(Context context) { + HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log", + android.os.Process.THREAD_PRIORITY_BACKGROUND); + backgroundThread.start(); + mWriteHandler = new Handler(backgroundThread.getLooper()) { + @Override + public void handleMessage(Message msg) { + NotificationRecord r = (NotificationRecord) msg.obj; + long nowMs = System.currentTimeMillis(); + switch (msg.what) { + case MSG_POST: + writeEvent(r.sbn.getPostTime(), EVENT_TYPE_POST, r, true); + break; + case MSG_CLICK: + writeEvent(nowMs, EVENT_TYPE_CLICK, r, false); + break; + case MSG_REMOVE: + writeEvent(nowMs, EVENT_TYPE_REMOVE, r, false); + break; + case MSG_DISMISS: + writeEvent(nowMs, EVENT_TYPE_DISMISS, r, false); + break; + default: + Log.wtf(TAG, "Unknown message type: " + msg.what); + break; + } + } + }; + mHelper = new SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) { + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + TAB_LOG + " (" + + "_id INTEGER PRIMARY KEY AUTOINCREMENT," + + COL_EVENT_USER_ID + " INT," + + COL_EVENT_TYPE + " INT," + + COL_EVENT_TIME + " INT," + + COL_KEY + " TEXT," + + COL_PKG + " TEXT," + + COL_NOTIFICATION_ID + " INT," + + COL_TAG + " TEXT," + + COL_WHEN_MS + " INT," + + COL_DEFAULTS + " INT," + + COL_FLAGS + " INT," + + COL_PRIORITY + " INT," + + COL_CATEGORY + " TEXT," + + COL_ACTION_COUNT + " INT" + + ")"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG); + onCreate(db); + } + }; + } + + public void logPosted(NotificationRecord notification) { + mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_POST, notification)); + } + + public void logClicked(NotificationRecord notification) { + mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_CLICK, notification)); + } + + public void logRemoved(NotificationRecord notification) { + mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_REMOVE, notification)); + } + + public void logDismissed(NotificationRecord notification) { + mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification)); + } + + public void printPostFrequencies(PrintWriter pw, String indent) { + SQLiteDatabase db = mHelper.getReadableDatabase(); + long nowMs = System.currentTimeMillis(); + String q = "SELECT " + + COL_EVENT_USER_ID + ", " + + COL_PKG + ", " + + // Bucket by day by looking at 'floor((nowMs - eventTimeMs) / dayMs)' + "CAST(((" + nowMs + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " + + "AS day, " + + "COUNT(*) AS cnt " + + "FROM " + TAB_LOG + " " + + "WHERE " + + COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + " " + + "GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG; + Cursor cursor = db.rawQuery(q, null); + try { + for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { + int userId = cursor.getInt(0); + String pkg = cursor.getString(1); + int day = cursor.getInt(2); + int count = cursor.getInt(3); + pw.println(indent + "post_frequency{user_id=" + userId + ",pkg=" + pkg + + ",day=" + day + ",count=" + count + "}"); + } + } finally { + cursor.close(); + } + } + + private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r, + boolean populateNotificationDetails) { + 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) { + putNotificationDetails(r, cv); + } + SQLiteDatabase db = mHelper.getWritableDatabase(); + if (db.insert(TAB_LOG, null, cv) < 0) { + Log.wtf(TAG, "Error while trying to insert values: " + cv); + } + sNumWrites++; + pruneIfNecessary(db); + } + + private void pruneIfNecessary(SQLiteDatabase db) { + // Prune if we haven't in a while. + long nowMs = System.currentTimeMillis(); + if (sNumWrites > PRUNE_MIN_WRITES || + nowMs - sLastPruneMs > PRUNE_MIN_DELAY_MS) { + sNumWrites = 0; + sLastPruneMs = nowMs; + long horizonStartMs = nowMs - HORIZON_MS; + int deletedRows = db.delete(TAB_LOG, COL_EVENT_TIME + " < ?", + new String[] { String.valueOf(horizonStartMs) }); + Log.d(TAG, "Pruned event entries: " + deletedRows); + } + } + + private static void putNotificationIdentifiers(NotificationRecord r, ContentValues outCv) { + outCv.put(COL_KEY, r.sbn.getKey()); + outCv.put(COL_PKG, r.sbn.getPackageName()); + } + + private static void putNotificationDetails(NotificationRecord r, ContentValues outCv) { + outCv.put(COL_NOTIFICATION_ID, r.sbn.getId()); + if (r.sbn.getTag() != null) { + outCv.put(COL_TAG, r.sbn.getTag()); + } + outCv.put(COL_WHEN_MS, r.sbn.getPostTime()); + outCv.put(COL_FLAGS, r.getNotification().flags); + outCv.put(COL_PRIORITY, r.getNotification().priority); + if (r.getNotification().category != null) { + outCv.put(COL_CATEGORY, r.getNotification().category); + } + outCv.put(COL_ACTION_COUNT, r.getNotification().actions != null ? + r.getNotification().actions.length : 0); + } + + public void dump(PrintWriter pw, String indent) { + printPostFrequencies(pw, indent); + } + } } diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java new file mode 100644 index 0000000..f2db791 --- /dev/null +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.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 com.android.server.pm; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; + +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * {@hide} + */ +public class BackgroundDexOptService { + + static final String TAG = "BackgroundDexOptService"; + + private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) { + onIdleStart(); + } else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) { + onIdleStop(); + } + } + }; + + final PackageManagerService mPackageManager; + + final AtomicBoolean mIdleTime = new AtomicBoolean(false); + + public BackgroundDexOptService(Context context) { + mPackageManager = (PackageManagerService)ServiceManager.getService("package"); + + IntentFilter idleMaintenanceFilter = new IntentFilter(); + idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START); + idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_END); + context.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL, + idleMaintenanceFilter, null, null); + } + + public boolean onIdleStart() { + Log.i(TAG, "onIdleStart"); + if (mPackageManager.isStorageLow()) { + return false; + } + final HashSet<String> pkgs = mPackageManager.getPackagesThatNeedDexOpt(); + if (pkgs == null) { + return false; + } + mIdleTime.set(true); + new Thread("BackgroundDexOptService_DexOpter") { + @Override + public void run() { + for (String pkg : pkgs) { + if (!mIdleTime.get()) { + break; + } + mPackageManager.performDexOpt(pkg, false); + } + } + }.start(); + return true; + } + + public void onIdleStop() { + Log.i(TAG, "onIdleStop"); + mIdleTime.set(false); + } +} diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 48e9737..bd28e04 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -239,8 +239,10 @@ public class LauncherAppsService extends ILauncherApps.Stub { private class MyPackageMonitor extends PackageMonitor { - /** Checks if user is a profile of or same as listeningUser. */ - private boolean isProfileOf(UserHandle user, UserHandle listeningUser, String debugMsg) { + /** Checks if user is a profile of or same as listeningUser. + * and the user is enabled. */ + private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser, + String debugMsg) { if (user.getIdentifier() == listeningUser.getIdentifier()) { if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg); return true; @@ -251,7 +253,8 @@ public class LauncherAppsService extends ILauncherApps.Stub { UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier()); if (userInfo == null || listeningUserInfo == null || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID - || userInfo.profileGroupId != listeningUserInfo.profileGroupId) { + || userInfo.profileGroupId != listeningUserInfo.profileGroupId + || !userInfo.isEnabled()) { if (DEBUG) { Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":" + debugMsg); @@ -276,7 +279,7 @@ public class LauncherAppsService extends ILauncherApps.Stub { for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isProfileOf(user, listeningUser, "onPackageAdded")) continue; + if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue; try { listener.onPackageAdded(user, packageName); } catch (RemoteException re) { @@ -295,7 +298,7 @@ public class LauncherAppsService extends ILauncherApps.Stub { for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isProfileOf(user, listeningUser, "onPackageRemoved")) continue; + if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue; try { listener.onPackageRemoved(user, packageName); } catch (RemoteException re) { @@ -314,7 +317,7 @@ public class LauncherAppsService extends ILauncherApps.Stub { for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isProfileOf(user, listeningUser, "onPackageModified")) continue; + if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue; try { listener.onPackageChanged(user, packageName); } catch (RemoteException re) { @@ -333,7 +336,7 @@ public class LauncherAppsService extends ILauncherApps.Stub { for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isProfileOf(user, listeningUser, "onPackagesAvailable")) continue; + if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue; try { listener.onPackagesAvailable(user, packages, isReplacing()); } catch (RemoteException re) { @@ -352,7 +355,7 @@ public class LauncherAppsService extends ILauncherApps.Stub { for (int i = 0; i < n; i++) { IOnAppsChangedListener listener = mListeners.getBroadcastItem(i); UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i); - if (!isProfileOf(user, listeningUser, "onPackagesUnavailable")) continue; + if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue; try { listener.onPackagesUnavailable(user, packages, isReplacing()); } catch (RemoteException re) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6a843a8..c8b61f1 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -23,17 +23,19 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import static android.system.OsConstants.S_IRWXU; +import static android.os.Process.PACKAGE_INFO_GID; +import static android.os.Process.SYSTEM_UID; import static android.system.OsConstants.S_IRGRP; -import static android.system.OsConstants.S_IXGRP; import static android.system.OsConstants.S_IROTH; +import static android.system.OsConstants.S_IRWXU; +import static android.system.OsConstants.S_IXGRP; import static android.system.OsConstants.S_IXOTH; -import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; +import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.removeInt; -import android.content.pm.PackageParser.*; +import com.android.internal.R; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; @@ -43,10 +45,13 @@ import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.IntentResolver; -import com.android.server.ServiceThread; - import com.android.server.LocalServices; +import com.android.server.ServiceThread; import com.android.server.Watchdog; +import com.android.server.pm.Settings.DatabaseVersion; +import com.android.server.storage.DeviceStorageMonitorInternal; +import com.android.server.storage.DeviceStorageMonitorInternal; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -82,6 +87,7 @@ import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; +import android.content.pm.PackageParser.ActivityIntentInfo; import android.content.pm.PackageParser; import android.content.pm.PackageStats; import android.content.pm.PackageUserState; @@ -125,6 +131,7 @@ import android.system.ErrnoException; import android.system.Os; import android.system.StructStat; import android.text.TextUtils; +import android.util.AtomicFile; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -135,6 +142,7 @@ import android.util.SparseArray; import android.util.Xml; import android.view.Display; +import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; @@ -144,7 +152,9 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.FilenameFilter; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.Certificate; @@ -163,14 +173,14 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import dalvik.system.DexFile; +import dalvik.system.StaleDexCacheError; import dalvik.system.VMRuntime; import libcore.io.IoUtils; -import com.android.internal.R; -import com.android.server.pm.Settings.DatabaseVersion; -import com.android.server.storage.DeviceStorageMonitorInternal; - /** * Keep track of all those .apks everywhere. * @@ -197,6 +207,7 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_PACKAGE_SCANNING = false; private static final boolean DEBUG_APP_DIR_OBSERVER = false; private static final boolean DEBUG_VERIFY = false; + private static final boolean DEBUG_DEXOPT = false; private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; @@ -287,7 +298,6 @@ public class PackageManagerService extends IPackageManager.Stub { final Context mContext; final boolean mFactoryTest; final boolean mOnlyCore; - final boolean mNoDexOpt; final DisplayMetrics mMetrics; final int mDefParseFlags; final String[] mSeparateProcesses; @@ -593,6 +603,139 @@ public class PackageManagerService extends IPackageManager.Stub { private final String mRequiredVerifierPackage; + private final PackageUsage mPackageUsage = new PackageUsage(); + + private class PackageUsage { + private static final int WRITE_INTERVAL + = (DEBUG_DEXOPT) ? 0 : 30*60*1000; // 30m in ms + + private final Object mFileLock = new Object(); + private final AtomicLong mLastWritten = new AtomicLong(0); + private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false); + + void write(boolean force) { + if (force) { + write(); + return; + } + if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL + && !DEBUG_DEXOPT) { + return; + } + if (mBackgroundWriteRunning.compareAndSet(false, true)) { + new Thread("PackageUsage_DiskWriter") { + @Override + public void run() { + try { + write(true); + } finally { + mBackgroundWriteRunning.set(false); + } + } + }.start(); + } + } + + private void write() { + synchronized (mPackages) { + synchronized (mFileLock) { + AtomicFile file = getFile(); + FileOutputStream f = null; + try { + f = file.startWrite(); + BufferedOutputStream out = new BufferedOutputStream(f); + FileUtils.setPermissions(file.getBaseFile().getPath(), 0660, SYSTEM_UID, PACKAGE_INFO_GID); + StringBuilder sb = new StringBuilder(); + for (PackageParser.Package pkg : mPackages.values()) { + if (pkg.mLastPackageUsageTimeInMills == 0) { + continue; + } + sb.setLength(0); + sb.append(pkg.packageName); + sb.append(' '); + sb.append((long)pkg.mLastPackageUsageTimeInMills); + sb.append('\n'); + out.write(sb.toString().getBytes(StandardCharsets.US_ASCII)); + } + out.flush(); + file.finishWrite(f); + } catch (IOException e) { + if (f != null) { + file.failWrite(f); + } + Log.e(TAG, "Failed to write package usage times", e); + } + } + } + mLastWritten.set(SystemClock.elapsedRealtime()); + } + + void readLP() { + synchronized (mFileLock) { + AtomicFile file = getFile(); + BufferedInputStream in = null; + try { + in = new BufferedInputStream(file.openRead()); + StringBuffer sb = new StringBuffer(); + while (true) { + String packageName = readToken(in, sb, ' '); + if (packageName == null) { + break; + } + String timeInMillisString = readToken(in, sb, '\n'); + if (timeInMillisString == null) { + throw new IOException("Failed to find last usage time for package " + + packageName); + } + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + continue; + } + long timeInMillis; + try { + timeInMillis = Long.parseLong(timeInMillisString.toString()); + } catch (NumberFormatException e) { + throw new IOException("Failed to parse " + timeInMillisString + + " as a long.", e); + } + pkg.mLastPackageUsageTimeInMills = timeInMillis; + } + } catch (FileNotFoundException expected) { + } catch (IOException e) { + Log.w(TAG, "Failed to read package usage times", e); + } finally { + IoUtils.closeQuietly(in); + } + } + mLastWritten.set(SystemClock.elapsedRealtime()); + } + + private String readToken(InputStream in, StringBuffer sb, char endOfToken) + throws IOException { + sb.setLength(0); + while (true) { + int ch = in.read(); + if (ch == -1) { + if (sb.length() == 0) { + return null; + } + throw new IOException("Unexpected EOF"); + } + if (ch == endOfToken) { + return sb.toString(); + } + sb.append((char)ch); + } + } + + private AtomicFile getFile() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + File fname = new File(systemDir, "package-usage.list"); + return new AtomicFile(fname); + } + } + class PackageHandler extends Handler { private boolean mBound = false; final ArrayList<HandlerParams> mPendingInstalls = @@ -1137,7 +1280,6 @@ public class PackageManagerService extends IPackageManager.Stub { mContext = context; mFactoryTest = factoryTest; mOnlyCore = onlyCore; - mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); mSettings = new Settings(context); mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, @@ -1223,10 +1365,6 @@ public class PackageManagerService extends IPackageManager.Stub { // Set flag to monitor and not change apk file paths when // scanning install directories. int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING; - if (mNoDexOpt) { - Slog.w(TAG, "Running ENG build: no pre-dexopt!"); - scanMode |= SCAN_NO_DEX; - } final HashSet<String> alreadyDexOpted = new HashSet<String>(); @@ -1245,7 +1383,7 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "No BOOTCLASSPATH found!"); } - boolean didDexOpt = false; + boolean didDexOptLibraryOrTool = false; final List<String> instructionSets = getAllInstructionSets(); @@ -1265,13 +1403,12 @@ public class PackageManagerService extends IPackageManager.Stub { } try { - if (dalvik.system.DexFile.isDexOptNeededInternal( - lib, null, instructionSet, false)) { + if (DexFile.isDexOptNeededInternal(lib, null, instructionSet, false)) { alreadyDexOpted.add(lib); // The list of "shared libraries" we have at this point is mInstaller.dexopt(lib, Process.SYSTEM_UID, true, instructionSet); - didDexOpt = true; + didDexOptLibraryOrTool = true; } } catch (FileNotFoundException e) { Slog.w(TAG, "Library not found: " + lib); @@ -1317,9 +1454,9 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } try { - if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) { + if (DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) { mInstaller.dexopt(path, Process.SYSTEM_UID, true, instructionSet); - didDexOpt = true; + didDexOptLibraryOrTool = true; } } catch (FileNotFoundException e) { Slog.w(TAG, "Jar not found: " + path); @@ -1330,7 +1467,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - if (didDexOpt) { + if (didDexOptLibraryOrTool) { pruneDexFiles(new File(dataDir, "dalvik-cache")); } @@ -1509,12 +1646,15 @@ public class PackageManagerService extends IPackageManager.Stub { // the correct library paths. updateAllSharedLibrariesLPw(); - for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) { adjustCpuAbisForSharedUserLPw(setting.packages, true /* do dexopt */, false /* force dexopt */, false /* defer dexopt */); } + // Now that we know all the packages we are keeping, + // read and update their last usage times. + mPackageUsage.readLP(); + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END, SystemClock.uptimeMillis()); Slog.i(TAG, "Time to scan packages: " @@ -1573,6 +1713,14 @@ public class PackageManagerService extends IPackageManager.Stub { // // Additionally, delete all dex files from the root directory // since there shouldn't be any there anyway. + // + // Note: This isn't as good an indicator as it used to be. It + // used to include the boot classpath but at some point + // DexFile.isDexOptNeeded started returning false for the boot + // class path files in all cases. It is very possible in a + // small maintenance release update that the library and tool + // jars may be unchanged but APK could be removed resulting in + // unused dalvik-cache files. File[] files = cacheDir.listFiles(); if (files != null) { for (File file : files) { @@ -1595,10 +1743,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public boolean isFirstBoot() { return !mRestoredSettings; } + @Override public boolean isOnlyCoreApps() { return mOnlyCore; } @@ -1895,6 +2045,7 @@ public class PackageManagerService extends IPackageManager.Stub { state, userId); } + @Override public boolean isPackageAvailable(String packageName, int userId) { if (!sUserManager.exists(userId)) return false; enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "is package available"); @@ -1932,6 +2083,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public String[] currentToCanonicalPackageNames(String[] names) { String[] out = new String[names.length]; // reader @@ -1944,6 +2096,7 @@ public class PackageManagerService extends IPackageManager.Stub { return out; } + @Override public String[] canonicalToCurrentPackageNames(String[] names) { String[] out = new String[names.length]; // reader @@ -2004,6 +2157,7 @@ public class PackageManagerService extends IPackageManager.Stub { return pi; } + @Override public PermissionInfo getPermissionInfo(String name, int flags) { // reader synchronized (mPackages) { @@ -2015,6 +2169,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public List<PermissionInfo> queryPermissionsByGroup(String group, int flags) { // reader synchronized (mPackages) { @@ -2038,6 +2193,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public PermissionGroupInfo getPermissionGroupInfo(String name, int flags) { // reader synchronized (mPackages) { @@ -2046,6 +2202,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public List<PermissionGroupInfo> getAllPermissionGroups(int flags) { // reader synchronized (mPackages) { @@ -2131,6 +2288,7 @@ public class PackageManagerService extends IPackageManager.Stub { } + @Override public void freeStorageAndNotify(final long freeStorageSize, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); @@ -2156,6 +2314,7 @@ public class PackageManagerService extends IPackageManager.Stub { }); } + @Override public void freeStorage(final long freeStorageSize, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); @@ -2277,6 +2436,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public String[] getSystemSharedLibraryNames() { Set<String> libSet; synchronized (mPackages) { @@ -2291,6 +2451,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public FeatureInfo[] getSystemAvailableFeatures() { Collection<FeatureInfo> featSet; synchronized (mPackages) { @@ -2309,6 +2470,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public boolean hasSystemFeature(String name) { synchronized (mPackages) { return mAvailableFeatures.containsKey(name); @@ -2323,6 +2485,7 @@ public class PackageManagerService extends IPackageManager.Stub { + " is not privileged to communicate with user=" + userId); } + @Override public int checkPermission(String permName, String pkgName) { synchronized (mPackages) { PackageParser.Package p = mPackages.get(pkgName); @@ -2340,6 +2503,7 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.PERMISSION_DENIED; } + @Override public int checkUidPermission(String permName, int uid) { synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); @@ -2514,18 +2678,21 @@ public class PackageManagerService extends IPackageManager.Stub { return added; } + @Override public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { return addPermissionLocked(info, false); } } + @Override public boolean addPermissionAsync(PermissionInfo info) { synchronized (mPackages) { return addPermissionLocked(info, true); } } + @Override public void removePermission(String name) { synchronized (mPackages) { checkPermissionTreeLP(name); @@ -2570,6 +2737,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void grantPermission(String packageName, String permissionName) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null); @@ -2599,6 +2767,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void revokePermission(String packageName, String permissionName) { int changedAppId = -1; @@ -2657,12 +2826,14 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public boolean isProtectedBroadcast(String actionName) { synchronized (mPackages) { return mProtectedBroadcasts.contains(actionName); } } + @Override public int checkSignatures(String pkg1, String pkg2) { synchronized (mPackages) { final PackageParser.Package p1 = mPackages.get(pkg1); @@ -2675,6 +2846,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public int checkUidSignatures(int uid1, int uid2) { // Map to base uids. uid1 = UserHandle.getAppId(uid1); @@ -2814,6 +2986,7 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.SIGNATURE_NO_MATCH; } + @Override public String[] getPackagesForUid(int uid) { uid = UserHandle.getAppId(uid); // reader @@ -2837,6 +3010,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public String getNameForUid(int uid) { // reader synchronized (mPackages) { @@ -2852,6 +3026,7 @@ public class PackageManagerService extends IPackageManager.Stub { return null; } + @Override public int getUidForSharedUser(String sharedUserName) { if(sharedUserName == null) { return -1; @@ -2866,6 +3041,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public int getFlagsForUid(int uid) { synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); @@ -3797,6 +3973,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) { ArrayList<ProviderInfo> finalList = null; @@ -3834,6 +4011,7 @@ public class PackageManagerService extends IPackageManager.Stub { return finalList; } + @Override public InstrumentationInfo getInstrumentationInfo(ComponentName name, int flags) { // reader @@ -3843,6 +4021,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public List<InstrumentationInfo> queryInstrumentation(String targetPackage, int flags) { ArrayList<InstrumentationInfo> finalList = @@ -4289,21 +4468,60 @@ public class PackageManagerService extends IPackageManager.Stub { } if (pkgs != null) { + // Filter out packages that aren't recently used. + // + // The exception is first boot of a non-eng device, which + // should do a full dexopt. + boolean eng = "eng".equals(SystemProperties.get("ro.build.type")); + if (eng || !isFirstBoot()) { + // TODO: add a property to control this? + long dexOptLRUThresholdInMinutes; + if (eng) { + dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds. + } else { + dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users. + } + long dexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000; + + int total = pkgs.size(); + int skipped = 0; + long now = System.currentTimeMillis(); + for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) { + PackageParser.Package pkg = i.next(); + long then = pkg.mLastPackageUsageTimeInMills; + if (then + dexOptLRUThresholdInMills < now) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " + + ((then == 0) ? "never" : new Date(then))); + } + i.remove(); + skipped++; + } + } + if (DEBUG_DEXOPT) { + Log.i(TAG, "Skipped optimizing " + skipped + " of " + total); + } + } + int i = 0; for (PackageParser.Package pkg : pkgs) { + i++; + if (DEBUG_DEXOPT) { + Log.i(TAG, "Optimizing app " + i + " of " + pkgs.size() + + ": " + pkg.packageName); + } if (!isFirstBoot()) { - i++; try { ActivityManagerNative.getDefault().showBootMessage( mContext.getResources().getString( - com.android.internal.R.string.android_upgrading_apk, + R.string.android_upgrading_apk, i, pkgs.size()), true); } catch (RemoteException e) { } } PackageParser.Package p = pkg; synchronized (mInstallLock) { - if (!p.mDidDexOpt) { + if (p.mDexOptNeeded) { performDexOptLI(p, false /* force dex */, false /* defer */, true /* include dependencies */); } @@ -4315,25 +4533,57 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public boolean performDexOpt(String packageName) { enforceSystemOrRoot("Only the system can request dexopt be performed"); - if (!mNoDexOpt) { - return false; - } + return performDexOpt(packageName, true); + } + + public boolean performDexOpt(String packageName, boolean updateUsage) { PackageParser.Package p; synchronized (mPackages) { p = mPackages.get(packageName); - if (p == null || p.mDidDexOpt) { + if (p == null) { + return false; + } + if (updateUsage) { + p.mLastPackageUsageTimeInMills = System.currentTimeMillis(); + } + mPackageUsage.write(); + if (!p.mDexOptNeeded) { return false; } } + synchronized (mInstallLock) { return performDexOptLI(p, false /* force dex */, false /* defer */, true /* include dependencies */) == DEX_OPT_PERFORMED; } } - private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet, boolean forceDex, - boolean defer, HashSet<String> done) { + public HashSet<String> getPackagesThatNeedDexOpt() { + HashSet<String> pkgs = null; + synchronized (mPackages) { + for (PackageParser.Package p : mPackages.values()) { + if (DEBUG_DEXOPT) { + Log.i(TAG, p.packageName + " mDexOptNeeded=" + p.mDexOptNeeded); + } + if (!p.mDexOptNeeded) { + continue; + } + if (pkgs == null) { + pkgs = new HashSet<String>(); + } + pkgs.add(p.packageName); + } + } + return pkgs; + } + + public void shutdown() { + mPackageUsage.write(true); + } + + private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet, + boolean forceDex, boolean defer, HashSet<String> done) { for (int i=0; i<libs.size(); i++) { PackageParser.Package libPkg; String libName; @@ -4358,8 +4608,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final int DEX_OPT_FAILED = -1; private int performDexOptLI(PackageParser.Package pkg, String instructionSetOverride, - boolean forceDex, - boolean defer, HashSet<String> done) { + boolean forceDex, boolean defer, HashSet<String> done) { final String instructionSet = instructionSetOverride != null ? instructionSetOverride : getAppInstructionSet(pkg.applicationInfo); @@ -4376,47 +4625,52 @@ public class PackageManagerService extends IPackageManager.Stub { boolean performed = false; if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { String path = pkg.mScanPath; - int ret = 0; try { - if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path, - pkg.packageName, instructionSet, defer)) { - if (!forceDex && defer) { - if (mDeferredDexOpt == null) { - mDeferredDexOpt = new HashSet<PackageParser.Package>(); - } - mDeferredDexOpt.add(pkg); - return DEX_OPT_DEFERRED; - } else { - Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName + - " (instructionSet=" + instructionSet + ")"); - - final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); - ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), + boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path, + pkg.packageName, + instructionSet, + defer); + // There are three basic cases here: + // 1.) we need to dexopt, either because we are forced or it is needed + // 2.) we are defering a needed dexopt + // 3.) we are skipping an unneeded dexopt + if (forceDex || (!defer && isDexOptNeededInternal)) { + Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); + final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg), pkg.packageName, instructionSet); - pkg.mDidDexOpt = true; - performed = true; + // Note that we ran dexopt, since rerunning will + // probably just result in an error again. + pkg.mDexOptNeeded = false; + if (ret < 0) { + return DEX_OPT_FAILED; + } + return DEX_OPT_PERFORMED; + } + if (defer && isDexOptNeededInternal) { + if (mDeferredDexOpt == null) { + mDeferredDexOpt = new HashSet<PackageParser.Package>(); } + mDeferredDexOpt.add(pkg); + return DEX_OPT_DEFERRED; } + pkg.mDexOptNeeded = false; + return DEX_OPT_SKIPPED; } catch (FileNotFoundException e) { Slog.w(TAG, "Apk not found for dexopt: " + path); - ret = -1; + return DEX_OPT_FAILED; } catch (IOException e) { Slog.w(TAG, "IOException reading apk: " + path, e); - ret = -1; - } catch (dalvik.system.StaleDexCacheError e) { + return DEX_OPT_FAILED; + } catch (StaleDexCacheError e) { Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e); - ret = -1; + return DEX_OPT_FAILED; } catch (Exception e) { Slog.w(TAG, "Exception when doing dexopt : ", e); - ret = -1; - } - if (ret < 0) { - //error from installer return DEX_OPT_FAILED; } } - - return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; + return DEX_OPT_SKIPPED; } private String getAppInstructionSet(ApplicationInfo info) { @@ -4705,7 +4959,7 @@ public class PackageManagerService extends IPackageManager.Stub { mResolveActivity.processName = "system:ui"; mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; - mResolveActivity.theme = com.android.internal.R.style.Theme_Holo_Dialog_Alert; + mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert; mResolveActivity.exported = true; mResolveActivity.enabled = true; mResolveInfo.activityInfo = mResolveActivity; @@ -7064,6 +7318,7 @@ public class PackageManagerService extends IPackageManager.Stub { return mMediaMounted || Environment.isExternalStorageEmulated(); } + @Override public PackageCleanItem nextPackageToClean(PackageCleanItem lastPackage) { // writer synchronized (mPackages) { @@ -7249,6 +7504,7 @@ public class PackageManagerService extends IPackageManager.Stub { } /* Called when a downloaded package installation has been confirmed by the user */ + @Override public void installPackage( final Uri packageURI, final IPackageInstallObserver observer, final int flags, final String installerPackageName) { @@ -7266,6 +7522,7 @@ public class PackageManagerService extends IPackageManager.Stub { installerPackageName, verificationParams, encryptionParams); } + @Override public void installPackageWithVerificationAndEncryption(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { @@ -7479,11 +7736,7 @@ public class PackageManagerService extends IPackageManager.Stub { null); PackageSetting pkgSetting; final int uid = Binder.getCallingUid(); - if (UserHandle.getUserId(uid) != userId) { - mContext.enforceCallingPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "installExistingPackage for user " + userId); - } + enforceCrossUserPermission(uid, userId, true, "installExistingPackage for user " + userId); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { return PackageManager.INSTALL_FAILED_USER_RESTRICTED; } @@ -7679,6 +7932,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void finishPackageInstall(int token) { enforceSystemOrRoot("Only the system is allowed to finish installs"); @@ -7750,6 +8004,7 @@ public class PackageManagerService extends IPackageManager.Stub { -1); } + @Override public void setInstallerPackageName(String targetPackage, String installerPackageName) { final int uid = Binder.getCallingUid(); // writer @@ -9705,22 +9960,22 @@ public class PackageManagerService extends IPackageManager.Stub { // Utility method used to move dex files during install. private int moveDexFilesLI(PackageParser.Package newPackage) { - int retCode; if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath, - getAppInstructionSet(newPackage.applicationInfo)); + final String instructionSet = getAppInstructionSet(newPackage.applicationInfo); + int retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath, + instructionSet); if (retCode != 0) { - if (mNoDexOpt) { - /* - * If we're in an engineering build, programs are lazily run - * through dexopt. If the .dex file doesn't exist yet, it - * will be created when the program is run next. - */ - Slog.i(TAG, "dex file doesn't exist, skipping move: " + newPackage.mPath); - } else { - Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath); - return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; - } + /* + * Programs may be lazily run through dexopt, so the + * source may not exist. However, something seems to + * have gone wrong, so note that dexopt needs to be + * run again and remove the source file. In addition, + * remove the target to make sure there isn't a stale + * file from a previous version of the package. + */ + newPackage.mDexOptNeeded = true; + mInstaller.rmdex(newPackage.mScanPath, instructionSet); + mInstaller.rmdex(newPackage.mPath, instructionSet); } } return PackageManager.INSTALL_SUCCEEDED; @@ -10684,6 +10939,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void deleteApplicationCacheFiles(final String packageName, final IPackageDataObserver observer) { mContext.enforceCallingOrSelfPermission( @@ -10736,6 +10992,7 @@ public class PackageManagerService extends IPackageManager.Stub { return true; } + @Override public void getPackageSizeInfo(final String packageName, int userHandle, final IPackageStatsObserver observer) { mContext.enforceCallingOrSelfPermission( @@ -10815,14 +11072,17 @@ public class PackageManagerService extends IPackageManager.Stub { } + @Override public void addPackageToPreferred(String packageName) { Slog.w(TAG, "addPackageToPreferred: this is now a no-op"); } + @Override public void removePackageFromPreferred(String packageName) { Slog.w(TAG, "removePackageFromPreferred: this is now a no-op"); } + @Override public List<PackageInfo> getPreferredPackages(int flags) { return new ArrayList<PackageInfo>(); } @@ -10850,6 +11110,7 @@ public class PackageManagerService extends IPackageManager.Stub { return Build.VERSION_CODES.CUR_DEVELOPMENT; } + @Override public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity, int userId) { addPreferredActivityInternal(filter, match, set, activity, true, userId); @@ -10886,6 +11147,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void replacePreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { if (filter.countActions() != 1) { @@ -10942,6 +11204,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void clearPackagePreferredActivities(String packageName) { final int uid = Binder.getCallingUid(); // writer @@ -11005,6 +11268,7 @@ public class PackageManagerService extends IPackageManager.Stub { return changed; } + @Override public void resetPreferredActivities(int userId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); @@ -11018,6 +11282,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public int getPreferredActivities(List<IntentFilter> outFilters, List<ComponentName> outActivities, String packageName) { @@ -11325,6 +11590,7 @@ public class PackageManagerService extends IPackageManager.Stub { new int[] {UserHandle.getUserId(packageUid)}); } + @Override public void setPackageStoppedState(String packageName, boolean stopped, int userId) { if (!sUserManager.exists(userId)) return; final int uid = Binder.getCallingUid(); @@ -11341,6 +11607,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public String getInstallerPackageName(String packageName) { // reader synchronized (mPackages) { @@ -11370,6 +11637,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void enterSafeMode() { enforceSystemOrRoot("Only the system can request entering safe mode"); @@ -11378,6 +11646,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override public void systemReady() { mSystemReady = true; @@ -11423,10 +11692,12 @@ public class PackageManagerService extends IPackageManager.Stub { sUserManager.systemReady(); } + @Override public boolean isSafeMode() { return mSafeMode; } + @Override public boolean hasSystemUidErrors() { return mHasSystemUidErrors; } @@ -11924,6 +12195,7 @@ public class PackageManagerService extends IPackageManager.Stub { /* * Update media status on PackageManager. */ + @Override public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) { int callingUid = Binder.getCallingUid(); if (callingUid != 0 && callingUid != Process.SYSTEM_UID) { @@ -12518,6 +12790,7 @@ public class PackageManagerService extends IPackageManager.Stub { }); } + @Override public boolean setInstallLocation(int loc) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS, null); @@ -12533,6 +12806,7 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } + @Override public int getInstallLocation() { return android.provider.Settings.Global.getInt(mContext.getContentResolver(), android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 3239b46..60c6313 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -734,6 +734,18 @@ public class UserManagerService extends IUserManager.Stub { writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS); writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_VPN); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_TETHERING); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_FACTORY_RESET); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADD_USER); + writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_APPS); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME); serializer.endTag(null, TAG_RESTRICTIONS); } serializer.endTag(null, TAG_USER); @@ -873,6 +885,19 @@ public class UserManagerService extends IUserManager.Stub { readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS); readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER); + readBoolean(parser, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_VPN); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_TETHERING); + readBoolean(parser, restrictions, UserManager.DISALLOW_FACTORY_RESET); + readBoolean(parser, restrictions, UserManager.DISALLOW_ADD_USER); + readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_APPS); + readBoolean(parser, restrictions, + UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA); + readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE); + readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME); } } } diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index 8fed79f..956e3e6 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -45,6 +45,7 @@ import android.os.storage.IMountService; import android.os.storage.IMountShutdownObserver; import com.android.internal.telephony.ITelephony; +import com.android.server.pm.PackageManagerService; import android.util.Log; import android.view.WindowManager; @@ -329,6 +330,14 @@ public final class ShutdownThread extends Thread { } } + Log.i(TAG, "Shutting down package manager..."); + + final PackageManagerService pm = (PackageManagerService) + ServiceManager.getService("package"); + if (pm != null) { + pm.shutdown(); + } + // Shutdown radios. shutdownRadios(MAX_RADIO_WAIT_TIME); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 022bdae..738ad32 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -74,6 +74,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub private boolean mMenuVisible = false; private int mImeWindowVis = 0; private int mImeBackDisposition; + private boolean mShowImeSwitcher; private IBinder mImeToken = null; private int mCurrentUserId; @@ -346,7 +347,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub } @Override - public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition) { + public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition, + final boolean showImeSwitcher) { enforceStatusBar(); if (SPEW) { @@ -360,11 +362,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub mImeWindowVis = vis; mImeBackDisposition = backDisposition; mImeToken = token; + mShowImeSwitcher = showImeSwitcher; mHandler.post(new Runnable() { public void run() { if (mBar != null) { try { - mBar.setImeWindowStatus(token, vis, backDisposition); + mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher); } catch (RemoteException ex) { } } @@ -512,6 +515,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub switches[2] = mMenuVisible ? 1 : 0; switches[3] = mImeWindowVis; switches[4] = mImeBackDisposition; + switches[7] = mShowImeSwitcher ? 1 : 0; binders.add(mImeToken); } switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4318b0e..e746c1a 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -299,6 +299,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { boolean mHasSurface = false; + boolean mNotOnAppsDisplay = false; DisplayContent mDisplayContent; /** When true this window can be displayed on screens owther than mOwnerUid's */ @@ -430,6 +431,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { } mRootToken = appToken; mAppToken = appToken.appWindowToken; + if (mAppToken != null) { + final DisplayContent appDisplay = getDisplayContent(); + mNotOnAppsDisplay = displayContent != appDisplay; + } mWinAnimator = new WindowStateAnimator(this); mWinAnimator.mAlpha = a.alpha; @@ -717,7 +722,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { } public DisplayContent getDisplayContent() { - return mAppToken == null ? mDisplayContent : getStack().getDisplayContent(); + return mAppToken == null || mNotOnAppsDisplay ? + mDisplayContent : getStack().getDisplayContent(); } public int getDisplayId() { diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp index 163692b..163225e 100644 --- a/services/core/jni/com_android_server_AssetAtlasService.cpp +++ b/services/core/jni/com_android_server_AssetAtlasService.cpp @@ -46,33 +46,16 @@ namespace android { // ---------------------------------------------------------------------------- static struct { - jfieldID mFinalizer; - jfieldID mNativeCanvas; + jmethodID safeCanvasSwap; } gCanvasClassInfo; -static struct { - jfieldID mNativeCanvas; -} gCanvasFinalizerClassInfo; - -#define GET_LONG(object, field) \ - env->GetLongField(object, field) - -#define SET_LONG(object, field, value) \ - env->SetLongField(object, field, value) +#define INVOKEV(object, method, ...) \ + env->CallVoidMethod(object, method, __VA_ARGS__) // ---------------------------------------------------------------------------- // Canvas management // ---------------------------------------------------------------------------- -static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) { - jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer); - SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>( - GET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas)); - SET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas, (long) newCanvas); - SET_LONG(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (long) newCanvas); - SkSafeUnref(previousCanvas); -} - static jlong com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, jobject, jobject canvas, jint width, jint height) { @@ -82,7 +65,7 @@ static jlong com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, job bitmap->eraseColor(0); SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (*bitmap)); - swapCanvasPtr(env, canvas, nativeCanvas); + INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); return reinterpret_cast<jlong>(bitmap); } @@ -92,7 +75,7 @@ static void com_android_server_AssetAtlasService_releaseCanvas(JNIEnv* env, jobj SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); SkCanvas* nativeCanvas = SkNEW(SkCanvas); - swapCanvasPtr(env, canvas, nativeCanvas); + INVOKEV(canvas, gCanvasClassInfo.safeCanvasSwap, (jlong)nativeCanvas, false); delete bitmap; } @@ -242,9 +225,9 @@ static jboolean com_android_server_AssetAtlasService_upload(JNIEnv* env, jobject var = env->FindClass(className); \ LOG_FATAL_IF(! var, "Unable to find class " className); -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find field " fieldName); +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(!var, "Unable to find method " methodName); const char* const kClassPathName = "com/android/server/AssetAtlasService"; @@ -261,12 +244,7 @@ int register_android_server_AssetAtlasService(JNIEnv* env) { jclass clazz; FIND_CLASS(clazz, "android/graphics/Canvas"); - GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer", - "Landroid/graphics/Canvas$CanvasFinalizer;"); - GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J"); - - FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer"); - GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J"); + GET_METHOD_ID(gCanvasClassInfo.safeCanvasSwap, clazz, "safeCanvasSwap", "(JZ)V"); return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7a0d1c7..4e22b2a 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -44,12 +44,13 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; -import android.net.ProxyProperties; +import android.net.ProxyInfo; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -96,9 +97,11 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.DateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -111,6 +114,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String DEVICE_POLICIES_XML = "device_policies.xml"; + private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component"; + private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long MS_PER_DAY = 86400 * 1000; @@ -180,6 +185,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final ArrayList<ActiveAdmin> mAdminList = new ArrayList<ActiveAdmin>(); + // This is the list of component allowed to start lock task mode. + final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>(); + public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } @@ -231,6 +239,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { static class ActiveAdmin { private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features"; private static final String TAG_DISABLE_CAMERA = "disable-camera"; + private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management"; + private static final String TAG_ACCOUNT_TYPE = "account-type"; private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested"; private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date"; private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout"; @@ -296,6 +306,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean encryptionRequested = false; boolean disableCamera = false; + Set<String> accountTypesWithManagementDisabled = new HashSet<String>(); // TODO: review implementation decisions with frameworks team boolean specifiesGlobalProxy = false; @@ -412,6 +423,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures)); out.endTag(null, TAG_DISABLE_KEYGUARD_FEATURES); } + if (!accountTypesWithManagementDisabled.isEmpty()) { + out.startTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT); + for (String ac : accountTypesWithManagementDisabled) { + out.startTag(null, TAG_ACCOUNT_TYPE); + out.attribute(null, ATTR_VALUE, ac); + out.endTag(null, TAG_ACCOUNT_TYPE); + } + out.endTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT); + } } void readFromXml(XmlPullParser parser) @@ -483,6 +503,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) { disabledKeyguardFeatures = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); + } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) { + int outerDepthDAM = parser.getDepth(); + int typeDAM; + while ((typeDAM=parser.next()) != XmlPullParser.END_DOCUMENT + && (typeDAM != XmlPullParser.END_TAG + || parser.getDepth() > outerDepthDAM)) { + if (typeDAM == XmlPullParser.END_TAG || typeDAM == XmlPullParser.TEXT) { + continue; + } + String tagDAM = parser.getName(); + if (TAG_ACCOUNT_TYPE.equals(tagDAM)) { + accountTypesWithManagementDisabled.add( + parser.getAttributeValue(null, ATTR_VALUE)); + } else { + Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM); + } + } } else { Slog.w(LOG_TAG, "Unknown admin tag: " + tag); } @@ -924,6 +961,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endTag(null, "active-password"); } + for (int i=0; i<policy.mLockTaskComponents.size(); i++) { + ComponentName component = policy.mLockTaskComponents.get(i); + out.startTag(null, LOCK_TASK_COMPONENTS_XML); + out.attribute(null, "name", component.flattenToString()); + out.endTag(null, LOCK_TASK_COMPONENTS_XML); + } + out.endTag(null, "policies"); out.endDocument(); @@ -973,6 +1017,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } type = parser.next(); int outerDepth = parser.getDepth(); + policy.mLockTaskComponents.clear(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { @@ -1025,6 +1070,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.mActivePasswordNonLetter = Integer.parseInt( parser.getAttributeValue(null, "nonletter")); XmlUtils.skipCurrentTag(parser); + } else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) { + policy.mLockTaskComponents.add + (ComponentName.unflattenFromString + (parser.getAttributeValue(null, "name"))); + XmlUtils.skipCurrentTag(parser); } else { Slog.w(LOG_TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); @@ -2532,7 +2582,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { exclusionList = exclusionList.trim(); ContentResolver res = mContext.getContentResolver(); - ProxyProperties proxyProperties = new ProxyProperties(data[0], proxyPort, exclusionList); + ProxyInfo proxyProperties = new ProxyInfo(data[0], proxyPort, exclusionList); if (!proxyProperties.isValid()) { Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString()); return; @@ -3023,6 +3073,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + @Override public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter, ComponentName activity) { synchronized (this) { @@ -3043,6 +3094,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + @Override public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) { synchronized (this) { if (who == null) { @@ -3168,4 +3220,217 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } + + @Override + public void enableSystemApp(ComponentName who, String packageName) { + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + int userId = UserHandle.getCallingUserId(); + long id = Binder.clearCallingIdentity(); + + try { + UserManager um = UserManager.get(mContext); + if (!um.getUserInfo(userId).isManagedProfile()) { + throw new IllegalStateException( + "Only call this method from a managed profile."); + } + + // TODO: Use UserManager::getProfileParent when available. + UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER); + + if (DBG) { + Slog.v(LOG_TAG, "installing " + packageName + " for " + + userId); + } + + IPackageManager pm = AppGlobals.getPackageManager(); + if (!isSystemApp(pm, packageName, primaryUser.id)) { + throw new IllegalArgumentException("Only system apps can be enabled this way."); + } + + // Install the app. + pm.installExistingPackageAsUser(packageName, userId); + + } catch (RemoteException re) { + // shouldn't happen + Slog.wtf(LOG_TAG, "Failed to install " + packageName, re); + } finally { + restoreCallingIdentity(id); + } + } + } + + @Override + public int enableSystemAppWithIntent(ComponentName who, Intent intent) { + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + int userId = UserHandle.getCallingUserId(); + long id = Binder.clearCallingIdentity(); + + try { + UserManager um = UserManager.get(mContext); + if (!um.getUserInfo(userId).isManagedProfile()) { + throw new IllegalStateException( + "Only call this method from a managed profile."); + } + + // TODO: Use UserManager::getProfileParent when available. + UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER); + + IPackageManager pm = AppGlobals.getPackageManager(); + List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + 0, // no flags + primaryUser.id); + + if (DBG) Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable); + int numberOfAppsInstalled = 0; + if (activitiesToEnable != null) { + for (ResolveInfo info : activitiesToEnable) { + if (info.activityInfo != null) { + + if (!isSystemApp(pm, info.activityInfo.packageName, primaryUser.id)) { + throw new IllegalArgumentException( + "Only system apps can be enabled this way."); + } + + + numberOfAppsInstalled++; + pm.installExistingPackageAsUser(info.activityInfo.packageName, userId); + } + } + } + return numberOfAppsInstalled; + } catch (RemoteException e) { + // shouldn't happen + Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent); + return 0; + } finally { + restoreCallingIdentity(id); + } + } + } + + private boolean isSystemApp(IPackageManager pm, String packageName, int userId) + throws RemoteException { + ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId); + return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0; + } + + @Override + public void setAccountManagementDisabled(ComponentName who, String accountType, + boolean disabled) { + if (!mHasFeature) { + return; + } + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + if (disabled) { + ap.accountTypesWithManagementDisabled.add(accountType); + } else { + ap.accountTypesWithManagementDisabled.remove(accountType); + } + saveSettingsLocked(UserHandle.getCallingUserId()); + } + } + + @Override + public String[] getAccountTypesWithManagementDisabled() { + if (!mHasFeature) { + return null; + } + synchronized (this) { + DevicePolicyData policy = getUserData(UserHandle.getCallingUserId()); + final int N = policy.mAdminList.size(); + HashSet<String> resultSet = new HashSet<String>(); + for (int i = 0; i < N; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); + resultSet.addAll(admin.accountTypesWithManagementDisabled); + } + return resultSet.toArray(new String[resultSet.size()]); + } + } + + /** + * Sets which componets may enter lock task mode. + * + * This function can only be called by the device owner or the profile owner. + * @param components The list of components allowed to enter lock task mode. + */ + public void setLockTaskComponents(ComponentName[] components) throws SecurityException { + // Get the package names of the caller. + int uid = Binder.getCallingUid(); + String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid); + + // Check whether any of the package name is the device owner or the profile owner. + for (int i=0; i<packageNames.length; i++) { + String packageName = packageNames[i]; + int userHandle = UserHandle.getUserId(uid); + String profileOwnerPackage = getProfileOwner(userHandle); + if (isDeviceOwner(packageName) || + (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) { + + // If a package name is the device owner or the profile owner, + // we update the component list. + DevicePolicyData policy = getUserData(userHandle); + policy.mLockTaskComponents.clear(); + if (components != null) { + for (int j=0; j<components.length; j++) { + ComponentName component = components[j]; + policy.mLockTaskComponents.add(component); + } + } + + // Store the settings persistently. + saveSettingsLocked(userHandle); + return; + } + } + throw new SecurityException(); + } + + /** + * This function returns the list of components allowed to start the task lock mode. + */ + public ComponentName[] getLockTaskComponents() { + int userHandle = UserHandle.USER_OWNER; + DevicePolicyData policy = getUserData(userHandle); + ComponentName[] tempArray = policy.mLockTaskComponents.toArray(new ComponentName[0]); + return tempArray; + } + + /** + * This function lets the caller know whether the given component is allowed to start the + * lock task mode. + * @param component The component to check + */ + public boolean isLockTaskPermitted(ComponentName component) { + // Get current user's devicepolicy + int uid = Binder.getCallingUid(); + int userHandle = UserHandle.getUserId(uid); + DevicePolicyData policy = getUserData(userHandle); + for (int i=0; i<policy.mLockTaskComponents.size(); i++) { + ComponentName lockTaskComponent = policy.mLockTaskComponents.get(i); + + // If the given component equals one of the component stored our device-owner-set + // list, we allow this component to start the lock task mode. + if (lockTaskComponent.getPackageName().equals(component.getPackageName())) { + return true; + } + } + return false; + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7c9f7a8..716823c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -73,6 +73,7 @@ import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; import com.android.server.notification.NotificationManagerService; import com.android.server.os.SchedulingPolicyService; +import com.android.server.pm.BackgroundDexOptService; import com.android.server.pm.Installer; import com.android.server.pm.LauncherAppsService; import com.android.server.pm.PackageManagerService; @@ -604,6 +605,14 @@ public final class SystemServer { if (!disableNetwork) { try { + Slog.i(TAG, "Network Score Service"); + networkScore = new NetworkScoreService(context); + ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore); + } catch (Throwable e) { + reportWtf("starting Network Score Service", e); + } + + try { Slog.i(TAG, "NetworkStats Service"); networkStats = new NetworkStatsService(context, networkManagement, alarm); ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats); @@ -641,6 +650,15 @@ public final class SystemServer { } try { + Slog.i(TAG, "Wi-Fi Scanning Service"); + mSystemServiceManager.startService( + "com.android.server.wifi.WifiScanningService"); + + } catch (Throwable e) { + reportWtf("starting Wi-Fi Scanning Service", e); + } + + try { Slog.i(TAG, "Connectivity Service"); connectivity = new ConnectivityService( context, networkManagement, networkStats, networkPolicy); @@ -652,14 +670,6 @@ public final class SystemServer { } try { - Slog.i(TAG, "Network Score Service"); - networkScore = new NetworkScoreService(context); - ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore); - } catch (Throwable e) { - reportWtf("starting Network Score Service", e); - } - - try { Slog.i(TAG, "Network Service Discovery Service"); serviceDiscovery = NsdService.create(context); ServiceManager.addService( @@ -962,6 +972,13 @@ public final class SystemServer { } catch (Throwable e) { Slog.e(TAG, "Failure starting TrustManagerService", e); } + + try { + Slog.i(TAG, "BackgroundDexOptService"); + new BackgroundDexOptService(context); + } catch (Throwable e) { + reportWtf("starting BackgroundDexOptService", e); + } } try { diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index af0d0ad..3e9cf43 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -232,6 +232,15 @@ <category android:name="com.android.test.hwui.TEST" /> </intent-filter> </activity> + + <activity + android:name="LooperAcceleration" + android:label="Misc/LooperAcceleration"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> <activity android:name="TextFadeActivity" diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java new file mode 100644 index 0000000..20d8e11 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LooperAcceleration.java @@ -0,0 +1,96 @@ +/* + * 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.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Canvas; +import android.os.Bundle; +import android.os.Looper; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.webkit.WebChromeClient; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.LinearLayout; + +public class LooperAcceleration extends Activity { + + static final boolean INCLUDE_WEBVIEW = false; + + static class IsAcceleratedView extends View { + + public IsAcceleratedView(Context context) { + super(context); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (canvas.isHardwareAccelerated()) { + canvas.drawARGB(0xFF, 0x00, 0xFF, 0x00); + } else { + canvas.drawARGB(0xFF, 0xFF, 0x00, 0x00); + } + } + + } + + private View makeView() { + LinearLayout layout = new LinearLayout(this); + layout.addView(new IsAcceleratedView(this), LayoutParams.MATCH_PARENT, 60); + + if (INCLUDE_WEBVIEW) { + WebView wv = new WebView(this); + wv.setWebViewClient(new WebViewClient()); + wv.setWebChromeClient(new WebChromeClient()); + wv.loadUrl("http://www.webkit.org/blog-files/3d-transforms/poster-circle.html"); + layout.addView(wv, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + } + return layout; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(makeView()); + + new Thread() { + @Override + public void run() { + Looper.prepare(); + final Context context = LooperAcceleration.this; + Dialog dlg = new Dialog(context); + dlg.addContentView(makeView(), new LayoutParams(300, 400)); + dlg.setCancelable(true); + dlg.setCanceledOnTouchOutside(true); + dlg.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + Looper.myLooper().quit(); + } + }); + dlg.setTitle("Not Looper.getMainLooper() check"); + dlg.show(); + Looper.loop(); + } + }.start(); + } +} diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java index 2e029f0..b7dcef7 100644 --- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java +++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java @@ -84,11 +84,12 @@ public class PlayerSession { Log.d(TAG, "Creating session for package " + mContext.getBasePackageName()); mSession = man.createSession("OneMedia"); mSession.addCallback(mCallback); - mPerformer = mSession.setTransportPerformerEnabled(); + mPerformer = mSession.getTransportPerformer(); mPerformer.addListener(new TransportListener()); mPerformer.setPlaybackState(mPlaybackState); + mSession.setFlags(Session.FLAG_HANDLES_TRANSPORT_CONTROLS); mSession.setRouteOptions(mRouteOptions); - mSession.publish(); + mSession.setActive(true); } public void onDestroy() { diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml index bb2bebf..d0f2a2d 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml @@ -14,7 +14,7 @@ limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:trigger="state_checked" android:versionCode="1" > + android:versionCode="1" > <size android:height="48dp" @@ -26,53 +26,11 @@ <group> <path - android:name="check" - android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z" - android:fill="?android:attr/colorControlActivated" /> - </group> - <group> - <path android:name="box1" - android:pathData="m127,171l37,38l33,-31l-37,-40l-1,3l-2,0l-30,30z" + android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z" android:fill="?android:attr/colorControlActivated" android:stroke="?android:attr/colorControlActivated" android:strokeLineCap="round" android:strokeLineJoin="round" /> </group> - <group> - <path - android:name="box2" - android:pathData="m127,171l37,38l33,-31l-37,-40l-1,3l-2,0l-30,30z" - android:rotation="46.757" - android:pivotX="162" - android:pivotY="173.5" - android:fill="?android:attr/colorControlNormal" - android:stroke="?android:attr/colorControlNormal" - android:strokeWidth="3" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - </group> - <group> - <path - android:name="box3" - android:pathData="m187,147l-1,55l-49,-1l2,-53l48,0z" - android:stroke="?android:attr/colorControlNormal" - android:strokeWidth="10" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - </group> - <group> - <path - android:name="box4" - android:pathData="m248,74l0,164l-177,0l1,-165l173,-1l3,2z" - android:stroke="?android:attr/colorControlNormal" - android:strokeWidth="30" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - </group> - - <animation - android:durations="300,100,0,300" - android:sequence="check,box1,box2,box3,box4" /> - </vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml index 49906d17..728624a 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml @@ -23,14 +23,6 @@ <group> <path - android:name="arrow" - android:pathData="M 100,225 L 100,115 L 130,115 L 70,15 L 10,115 L 40,115 L 40,225 z" - android:fill="#ffffffff" - android:stroke="#FF00FF00" - android:strokeWidth="1"/> - </group> - <group> - <path android:name="house" android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z" android:fill="#ff440000" @@ -42,5 +34,4 @@ android:trimPathStart=".1" android:trimPathEnd=".9"/> </group> - <animation android:sequence="arrow,house"/> </vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml index 137049d..1792683 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml @@ -71,67 +71,4 @@ android:fill="#ff88ff" /> </group> - <group> - <path - android:name="clip1" - android:pathData=" - M 0, 0 - l 7.3, 0 - l 0, 12.25 - l -7.3, 0 - z" - android:clipToPath="true" - android:rotation="-30" - android:pivotX="3.65" - android:pivotY="6.125" - /> - <path - android:name="one" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 - l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 - l -5.046875,0.0 0.0,-1.0Z" - android:fill="#ff88ff" - /> - <path - android:name="clip2" - android:pathData=" - M 0, 12.25 - l 7.3, 0 - l 0, 12.25 - l -7.3, 0 - z" - android:clipToPath="true" - android:rotation="-30" - android:pivotX="3.65" - android:pivotY="6.125" - /> - <path - android:name="two" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 - q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 - q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 - q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 - q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875 - q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875 - q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 - q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 - q -0.78125024,0.8125 -2.2187502,2.265625Z" - android:fill="#ff88ff" - /> - </group> - - - <animation - android:sequence="one,one" - android:durations="4000"/> - <animation - android:sequence="two,two" - android:durations="4000"/> - <animation - android:sequence="clip1,clip1" - android:durations="4000"/> - <animation - android:sequence="clip2,clip2" - android:durations="4000"/> - </vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml index cffb73f..90694fb 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml @@ -65,62 +65,4 @@ android:fill="#ff88ff" /> </group> - <group> - <path - android:name="clip1" - android:pathData=" - M 3.65, 6.125 - m -6, 0 - a 6,6 0 1,0 12,0 - a 6,6 0 1,0 -12,0z" - android:clipToPath="true" - android:fill="#332233" - /> - <path - android:name="one" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 - l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 - l -5.046875,0.0 0.0,-1.0Z" - android:fill="#ff88ff" - /> - <path - android:name="clip2" - android:pathData=" - M 3.65, 6.125 - m -.001, 0 - a .001,.001 0 1,0 .002,0 - a .001,.001 0 1,0 -.002,0z" - android:clipToPath="true" - android:fill="#662233" - /> - <path - android:name="two" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 - q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 - q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 - q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 - q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875 - q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875 - q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 - q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 - q -0.78125024,0.8125 -2.2187502,2.265625Z" - android:fill="#ff88ff" - /> - </group> - - - - <animation - android:sequence="one,one" - android:durations="4000"/> - <animation - android:sequence="two,two" - android:durations="4000"/> - <animation - android:sequence="clip1,clip1" - android:durations="4000"/> - <animation - android:sequence="clip2,clip2" - android:durations="4000"/> - </vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml index 0be6755..c6595fa 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml @@ -44,95 +44,4 @@ q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 q -0.78125024,0.8125 -2.2187502,2.265625Z" /> </group> - <group> - <path - android:name="one" - android:fill="#ffff00" - android:fillOpacity="0" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 -l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 -l -5.046875,0.0 0.0,-1.0Z" /> - <path - android:name="two" - android:fill="#ffff00" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 - q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 - q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 - q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 - q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875 - q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875 - q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 - q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 - q -0.78125024,0.8125 -2.2187502,2.265625Z" /> - </group> - <group> - <path - android:name="two" - android:fill="#ffff00" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 - q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 - q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 - q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 - q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875 - q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875 - q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 - q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 - q -0.78125024,0.8125 -2.2187502,2.265625Z" /> - <path - android:name="three" - android:fill="#ffff00" - android:fillOpacity="0" - android:pathData="M 5.103125,6.003125q 0.84375,0.1875 1.3125,0.765625 0.484375,0.5625 0.484375,1.40625 - q 0.0,1.296875 -0.890625,2.015625 -0.890625,0.703125 -2.53125,0.703125 - q -0.546875,0.0 -1.140625,-0.109375 -0.5781251,-0.109375 -1.1875001,-0.328125 - l 0.0,-1.140625q 0.484375,0.28125 1.0625001,0.4375 0.59375,0.140625 1.234375,0.140625 - q 1.109375,0.0 1.6875,-0.4375 0.59375,-0.4375 0.59375,-1.28125 - q 0.0,-0.765625 -0.546875,-1.203125 -0.546875,-0.4375 -1.5,-0.4375 - l -1.03125,0.0 0.0,-0.96875 1.078125,0.0q 0.859375,0.0 1.328125,-0.34375 - q 0.46875,-0.359375 0.46875,-1.015625 0.0,-0.671875 -0.484375,-1.03125 - q -0.46875,-0.359375 -1.359375,-0.359375 -0.5,0.0 -1.0625,0.109375 - q -0.546875,0.09375 -1.2187501,0.3125l 0.0,-1.046875q 0.6875001,-0.1875 1.2656251,-0.28125 - q 0.59375,-0.09375 1.109375,-0.09375 1.359375,0.0 2.140625,0.609375 - q 0.78125,0.609375 0.78125,1.65625 0.0,0.734375 -0.421875,1.234375 - q -0.40625,0.5 -1.171875,0.6875Z" /> - </group> - <group> - <path - android:name="two" - android:fill="#ffff00" - android:fillOpacity="0" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 - q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 - q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 - q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 - q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875 - q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875 - q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 - q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 - q -0.78125024,0.8125 -2.2187502,2.265625Z" /> - <path - android:name="three" - android:fill="#ffff00" - android:pathData="M 5.103125,6.003125q 0.84375,0.1875 1.3125,0.765625 0.484375,0.5625 0.484375,1.40625 - q 0.0,1.296875 -0.890625,2.015625 -0.890625,0.703125 -2.53125,0.703125 - q -0.546875,0.0 -1.140625,-0.109375 -0.5781251,-0.109375 -1.1875001,-0.328125 - l 0.0,-1.140625q 0.484375,0.28125 1.0625001,0.4375 0.59375,0.140625 1.234375,0.140625 - q 1.109375,0.0 1.6875,-0.4375 0.59375,-0.4375 0.59375,-1.28125 - q 0.0,-0.765625 -0.546875,-1.203125 -0.546875,-0.4375 -1.5,-0.4375 - l -1.03125,0.0 0.0,-0.96875 1.078125,0.0q 0.859375,0.0 1.328125,-0.34375 - q 0.46875,-0.359375 0.46875,-1.015625 0.0,-0.671875 -0.484375,-1.03125 - q -0.46875,-0.359375 -1.359375,-0.359375 -0.5,0.0 -1.0625,0.109375 - q -0.546875,0.09375 -1.2187501,0.3125l 0.0,-1.046875q 0.6875001,-0.1875 1.2656251,-0.28125 - q 0.59375,-0.09375 1.109375,-0.09375 1.359375,0.0 2.140625,0.609375 - q 0.78125,0.609375 0.78125,1.65625 0.0,0.734375 -0.421875,1.234375 - q -0.40625,0.5 -1.171875,0.6875Z" /> - </group> - - <animation - android:durations="2000,0,2000" - android:sequence="one,one,three,three" /> - <animation - android:durations="2000,0,2000" - android:sequence="two,two,two,two" /> - </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml index 73ff5e2..850de28 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml @@ -23,29 +23,6 @@ android:viewportHeight="700"/> <group> - </group> - <path android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z" - android:name="path2451" - android:stroke="#FF000000" - android:strokeWidth="30.65500000000000"/> - <path android:pathData="M 365.015 311.066" - android:name="path2453" - android:stroke="#FF000000" - android:strokeWidth="30.655000000000001"/> - <path android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928" - android:name="path2455" - android:stroke="#FF000000" - android:fill="#FF0000FF" - android:strokeWidth="30.655000000000001"/> - <path android:pathData="M 170.515 451.566L 305.61 313.46" - android:name="path2457" - android:stroke="#000000" - android:strokeWidth="30.655000000000001"/> - <path android:pathData="M 557.968 449.974L 426.515 315.375" - android:name="path2459" - android:stroke="#000000" - android:strokeWidth="30.655000000000001"/> - <group> <path android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z" android:name="path2451" android:stroke="#FF000000" @@ -68,9 +45,4 @@ android:stroke="#000000" android:strokeWidth="30.655000000000001"/> </group> - - <animation android:sequence="path2451,path2451" - android:durations="1000"/> - - </vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml index 99d37ef..7c7e679 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml @@ -19,14 +19,7 @@ <viewport android:viewportWidth="140" android:viewportHeight="110"/> - <group> - <path - android:name="menu" - android:pathData="M 20,20 l 100,0 0,10 -100,0 z - M 20,50 l 100,0 0,10 -100,0 z - M 20,80 l 0,-10 100,0 0,10 z" - android:fill="#ffffffff"/> - </group> + <group> <path android:name="back" @@ -34,12 +27,6 @@ M 27,50 l 97,0 0,10 -97,0 z M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z" android:fill="#ffffffff" - android:rotation="180" - android:pivotX="70" - android:pivotY="55" /> </group> - <animation android:sequence="menu,back"/> - - </vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml index f8a03d7..59f7459 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml @@ -31,18 +31,4 @@ android:strokeWidth="1"/> </group> - <group> - <path - android:name="pie2" - android:pathData="M564.441,287A280.868,280.868 0 1,1 564.441,285L284.493,286.29Z" - android:fill="#FFccaa00" - android:stroke="#FF000000" - android:strokeWidth="10" - android:pivotX="90" - android:pivotY="100"/> - </group> - - <animation android:sequence="pie1,pie2"/> - - </vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml index b3c91a8..2e379d6 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml @@ -25,12 +25,6 @@ <group> <path - android:name="arrow" - android:fill="#ffffffff" - android:pathData="M 20,20 l 60,0 0,140 -60,0 z M 120,20 l 60,0 0,140 -60,0 z" /> - </group> - <group> - <path android:name="house" android:fill="#ffffffff" android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z" @@ -39,6 +33,4 @@ android:rotation="90" /> </group> - <animation android:sequence="arrow,house" /> - </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml index 7aca169..8484e9e 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml @@ -21,8 +21,8 @@ android:width="64dp" /> <viewport - android:viewportHeight="200" - android:viewportWidth="200" /> + android:viewportWidth="200" + android:viewportHeight="200"/> <group> <path @@ -31,24 +31,6 @@ android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" /> <path android:name="bar2" - android:fill="#FF555555" - android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" /> - <path - android:name="bar1" - android:fill="#FF555555" - android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" /> - <path - android:name="bar0" - android:fill="#FF555555" - android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" /> - </group> - <group> - <path - android:name="bar3" - android:fill="#FFFFFFFF" - android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" /> - <path - android:name="bar2" android:fill="#FFFFFFFF" android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" /> <path @@ -60,54 +42,5 @@ android:fill="#FF555555" android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" /> </group> - <group> - <path - android:name="bar3" - android:fill="#FFFFFFFF" - android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" /> - <path - android:name="bar2" - android:fill="#FFFFFFFF" - android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" /> - <path - android:name="bar1" - android:fill="#FFFFFFFF" - android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" /> - <path - android:name="bar0" - android:fill="#FF555555" - android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" /> - </group> - <group> - <path - android:name="bar3" - android:fill="#FFFFFFFF" - android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" /> - <path - android:name="bar2" - android:fill="#FFFFFFFF" - android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" /> - <path - android:name="bar1" - android:fill="#FFFFFFFF" - android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" /> - <path - android:name="bar0" - android:fill="#FFFFFFFF" - android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" /> - </group> - - <animation - android:durations="500,500,500" - android:sequence="bar0,bar0,bar0,bar0" /> - <animation - android:durations="500,500,500" - android:sequence="bar1,bar1,bar1,bar1" /> - <animation - android:durations="500,500,500" - android:sequence="bar2,bar2,bar2,bar2" /> - <animation - android:durations="500,500,500" - android:sequence="bar3,bar3,bar3,bar3" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml index a4403c5..2b6c5d3 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml @@ -36,25 +36,5 @@ android:fill="#FFFF0000" android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" /> </group> - <group> - <path - android:name="battery" - android:fill="#ff8833" - android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z" - android:rotation="0" - android:stroke="#3388ff" - android:strokeWidth="1" /> - <path - android:name="spark" - android:fill="#FFFF0000" - android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" /> - </group> - - <animation - android:durations="2000" - android:sequence="spark,spark" /> - <animation - android:durations="2000" - android:sequence="battery,battery" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml index 207879d..681eb4f 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml @@ -40,48 +40,5 @@ android:pivotY="300" android:rotation="0" /> </group> - <group> - <path - android:name="v" - android:pathData="M300,70 l 0,-70 70,70 -70,70z" - android:pivotX="300" - android:pivotY="300" - android:rotation="360" /> - <path - android:name="pie2" - android:pathData="M300,70 a230,230 0 1,0 1,0 z" - android:pivotX="300" - android:pivotY="300" - android:rotation="360" - android:stroke="#FF00FF00" - android:strokeLineCap="round" - android:strokeWidth="70" - android:trimPathEnd=".5" - android:trimPathOffset="0" - android:trimPathStart="0" /> - </group> - - <animation - android:animate="easeInOut" - android:durations="2000" - android:repeatCount="-1" - android:repeatStyle="forward" - android:sequence="pie1,pie2" - android:startOffset="500" /> - <animation - android:animate="easeInOut" - android:durations="2000" - android:repeatCount="-1" - android:repeatStyle="forward" - android:sequence="v,v" - android:startOffset="500" /> - <animation - android:animate="easeInOut" - android:durations="2800" - android:limitTo="trimPathEnd" - android:repeatCount="-1" - android:repeatStyle="reverse" - android:sequence="pie1,pie2" - android:startOffset="500" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml index 4a2ed90..ef1b8e4 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml @@ -40,40 +40,5 @@ android:stroke="#FF0000FF" android:strokeWidth="5" /> </group> - <group> - <path - android:name="pie2" - android:fill="#ffff0000" - android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z" - android:pivotX="300" - android:pivotY="200" - android:rotation="360" - android:stroke="#FF00FF00" - android:strokeWidth="10" /> - <path - android:name="half" - android:fill="#FFFFFF00" - android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z" - android:pivotX="300" - android:pivotY="200" - android:rotation="-360" - android:stroke="#FF0000FF" - android:strokeWidth="5" /> - </group> - - <animation - android:animate="easeInOut" - android:durations="1000" - android:repeatCount="2" - android:repeatStyle="forward" - android:sequence="pie1,pie2" - android:startOffset="500" /> - <animation - android:animate="easeInOut" - android:durations="1000" - android:repeatCount="5" - android:repeatStyle="forward" - android:sequence="half,half" - android:startOffset="500" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml index 6ebd56b..77bf723 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml @@ -25,17 +25,6 @@ <group> <path - android:name="pie1" - android:pathData="M200,450 l 50,-25 - a25,25 -30 0,1 100,-50 l 50,-25 - a25,50 -30 0,1 100,-50 l 50,-25 - a25,75 -30 0,1 100,-50 l 50,-25 - a25,100 -30 0,1 100,-50 l 50,-25" - android:stroke="#FF00FF00" - android:strokeWidth="10" /> - </group> - <group> - <path android:name="pie2" android:pathData="M200,350 l 50,-25 a25,12 -30 0,1 100,-50 l 50,-25 @@ -49,6 +38,4 @@ android:strokeWidth="10" /> </group> - <animation android:sequence="pie1,pie2" /> - </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml index 3c92d25..df5838c 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml @@ -25,14 +25,6 @@ <group> <path - android:name="arrow" - android:fill="#ffffffff" - android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" - android:stroke="#FFFF0000" - android:strokeWidth="1" /> - </group> - <group> - <path android:name="house" android:fill="#ff440000" android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" @@ -43,6 +35,4 @@ android:strokeWidth="10" /> </group> - <animation android:sequence="arrow,house" /> - </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml index 7e757a5..0bdcda5 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml @@ -25,13 +25,6 @@ <group> <path - android:name="arrow" - android:pathData="M 100,10 v 180 M 10,100 h 180" - android:stroke="#FF00FF00" - android:strokeWidth="1" /> - </group> - <group> - <path android:name="house" android:pathData="M 100,10 v 90 M 10,100 h 90" android:pivotX="100" @@ -41,6 +34,4 @@ android:strokeWidth="10" /> </group> - <animation android:sequence="arrow,house" /> - </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml index 9427652..4453ae4 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml @@ -23,14 +23,6 @@ <group> <path - android:name="arrow" - android:pathData="M200,300 Q400,50 600,300 T1000,300" - android:stroke="#FF00FF00" - android:strokeWidth="1"/> - </group> - - <group> - <path android:name="house" android:pathData="M200,300 Q400,50 600,300 T1000,300" android:stroke="#FFFF0000" @@ -40,7 +32,4 @@ android:pivotY="300"/> </group> - <animation android:sequence="arrow,house"/> - - </vector> diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml index 69212f5..dfae9ac 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml @@ -25,21 +25,13 @@ <group> <path - android:name="arrow" - android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" - android:stroke="#FFFFFF00" - android:strokeWidth="10" /> - </group> - <group> - <path android:name="house" android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" android:pivotX="250" android:pivotY="200" android:rotation="360" + android:stroke="#FFFFFF00" android:strokeWidth="10" /> </group> - <animation android:sequence="arrow,house" /> - </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml index 2dca48d..a890fd6 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml @@ -25,13 +25,6 @@ <group> <path - android:name="arrow" - android:pathData="M10,300 Q400,50 600,300 T1000,300" - android:stroke="#FF00FFFF" - android:strokeWidth="40" /> - </group> - <group> - <path android:name="house" android:pathData="M10,300 Q400,550 600,300 T1000,300" android:pivotX="90" @@ -40,6 +33,4 @@ android:strokeWidth="60" /> </group> - <animation android:sequence="arrow,house" /> - </vector>
\ No newline at end of file diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index bb05d45..aede236 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -71,7 +71,7 @@ public final class Canvas_Delegate { * Returns the native delegate associated to a given {@link Canvas} object. */ public static Canvas_Delegate getDelegate(Canvas canvas) { - return sManager.getDelegate(canvas.mNativeCanvas); + return sManager.getDelegate(canvas.getNativeCanvas()); } /** @@ -102,7 +102,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static boolean isOpaque(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return false; } @@ -113,7 +113,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getWidth(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return 0; } @@ -124,7 +124,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getHeight(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return 0; } @@ -135,7 +135,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -146,7 +146,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void rotate(Canvas thisCanvas, float degrees) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -157,7 +157,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -168,7 +168,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -204,7 +204,7 @@ public final class Canvas_Delegate { /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right, float bottom) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return false; } @@ -227,7 +227,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int save(Canvas thisCanvas, int saveFlags) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return 0; } @@ -238,7 +238,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void restore(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -249,7 +249,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getSaveCount(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return 0; } @@ -260,7 +260,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -287,7 +287,7 @@ public final class Canvas_Delegate { /*package*/ static void drawLines(Canvas thisCanvas, final float[] pts, final int offset, final int count, Paint paint) { - draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/, + draw(thisCanvas.getNativeCanvas(), paint.mNativePaint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) { diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/wifi/java/android/net/wifi/IWifiScanner.aidl index c6c0719..fef2d11 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/wifi/java/android/net/wifi/IWifiScanner.aidl @@ -1,22 +1,27 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (c) 2012, The Android Open Source Project +/* + * Copyright (C) 2008 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 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -*/ ---> -<resources> - <!-- Layout parameters for the notification panel --> - <dimen name="notification_panel_margin_bottom">0dp</dimen> - <dimen name="notification_panel_margin_left">32dp</dimen> -</resources> + */ + +package android.net.wifi; + +import android.os.Messenger; + +/** + * {@hide} + */ +interface IWifiScanner +{ + Messenger getMessenger(); +} diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index d7ecaff..1cb9546 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -56,6 +56,13 @@ public class ScanResult implements Parcelable { public long timestamp; /** + * Timestamp representing date when this result was last seen, in milliseconds from 1970 + * {@hide} + */ + public long seen; + + + /** * The approximate distance to the AP in centimeter, if available. Else * {@link UNSPECIFIED}. * {@hide} @@ -114,9 +121,17 @@ public class ScanResult implements Parcelable { timestamp = source.timestamp; distanceCm = source.distanceCm; distanceSdCm = source.distanceSdCm; + seen = source.seen; } } + /** empty scan result + * + * {@hide} + * */ + public ScanResult() { + } + @Override public String toString() { StringBuffer sb = new StringBuffer(); diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 6562462..ce8c8b8 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -21,6 +21,7 @@ import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; +import java.util.HashMap; import java.util.BitSet; /** @@ -302,6 +303,156 @@ public class WifiConfiguration implements Parcelable { /** * @hide + * dhcp server MAC address if known + */ + public String dhcpServer; + + /** + * @hide + * default Gateway MAC address if known + */ + public String defaultGwMacAddress; + + /** + * @hide + * BSSID list on which this configuration was seen. + * TODO: prevent this list to grow infinitely, age-out the results + */ + public HashMap<String, ScanResult> scanResultCache; + + /** @hide **/ + public static int INVALID_RSSI = -127; + + /** + * @hide + * A summary of the RSSI and Band status for that configuration + * This is used as a temporary value by the auto-join controller + */ + public final class Visibility + { + public int rssi5; // strongest 5GHz RSSI + public int rssi24; // strongest 2.4GHz RSSI + public int num5; // number of BSSIDs on 5GHz + public int num24; // number of BSSIDs on 2.4GHz + public long age5; // timestamp of the strongest 5GHz BSSID (last time it was seen) + public long age24; // timestamp of the strongest 2.4GHz BSSID (last time it was seen) + public Visibility() + { + rssi5 = INVALID_RSSI; + rssi24 = INVALID_RSSI; + } + public Visibility(Visibility source) + { + rssi5 = source.rssi5; + rssi24 = source.rssi24; + age24 = source.age24; + age5 = source.age5; + num24 = source.num24; + num5 = source.num5; + } + } + + /** @hide + * Cache the visibility status of this configuration. + * Visibility can change at any time depending on scan results availability. + * Owner of the WifiConfiguration is responsible to set this field based on + * recent scan results. + ***/ + public Visibility visibility; + + /** @hide + * calculate and set Visibility for that configuration. + * + * age in milliseconds: we will consider only ScanResults that are more recent, + * i.e. younger. + ***/ + public Visibility setVisibility(long age) { + if (scanResultCache == null) { + visibility = null; + return null; + } + + Visibility status = new Visibility(); + + long now_ms = System.currentTimeMillis(); + for(ScanResult result : scanResultCache.values()) { + if (result.seen == 0) + continue; + + if ((result.frequency > 4900) && (result.frequency < 5900)) { + //strictly speaking: [4915, 5825] + //number of known BSSID on 5GHz band + status.num5 = status.num5 + 1; + } else if ((result.frequency > 2400) && (result.frequency < 2500)) { + //strictly speaking: [2412, 2482] + //number of known BSSID on 2.4Ghz band + status.num24 = status.num24 + 1; + } + + if ((now_ms - result.seen) > age) continue; + + if ((result.frequency > 4900) && (result.frequency < 5900)) { + if (result.level > status.rssi5) { + status.rssi5 = result.level; + status.age5 = result.seen; + } + } else if ((result.frequency > 2400) && (result.frequency < 2500)) { + if (result.level > status.rssi24) { + status.rssi24 = result.level; + status.age24 = result.seen; + } + } + } + visibility = status; + return status; + } + + /** @hide */ + public static final int AUTO_JOIN_ENABLED = 0; + /** @hide */ + public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE = 1; + /** + * @hide + */ + public int autoJoinStatus; + + /** + * @hide + * Indicate that a WifiConfiguration is temporary and should not be saved + * nor considered by AutoJoin. + */ + public boolean ephemeral; + + /** + * @hide + * Connect choices + * + * remember the keys identifying the known WifiConfiguration over which this configuration + * was preferred by user or a "WiFi Network Management app", that is, + * a WifiManager.CONNECT_NETWORK or SELECT_NETWORK was received while this configuration + * was visible to the user: + * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP + * + * The integer represents the configuration's RSSI at that time (useful?) + * + * The overall auto-join algorithm make use of past connect choice so as to sort configuration, + * the exact algorithm still fluctuating as of 5/7/2014 + * + */ + public HashMap<String, Integer> connectChoices; + + /** + * @hide + * Linked Configurations: represent the set of Wificonfigurations that are equivalent + * regarding roaming and auto-joining. + * The linked configuration may or may not have same SSID, and may or may not have same + * credentials. + * For instance, linked configurations will have same defaultGwMacAddress or same dhcp server. + */ + public HashMap<String, Integer> linkedConfigurations; + + /** + * @hide */ public enum ProxySettings { /* No proxy is to be used. Any existing proxy settings @@ -346,6 +497,7 @@ public class WifiConfiguration implements Parcelable { ipAssignment = IpAssignment.UNASSIGNED; proxySettings = ProxySettings.UNASSIGNED; linkProperties = new LinkProperties(); + autoJoinStatus = AUTO_JOIN_ENABLED; } /** @@ -369,6 +521,32 @@ public class WifiConfiguration implements Parcelable { // TODO: Add more checks return true; + + } + + /** + * most recent time we have seen this configuration + * @return most recent scanResult + * @hide + */ + public ScanResult lastSeen() { + ScanResult mostRecent = null; + + if (scanResultCache == null) { + return null; + } + + for (ScanResult result : scanResultCache.values()) { + if (mostRecent == null) { + if (result.seen != 0) + mostRecent = result; + } else { + if (result.seen > mostRecent.seen) { + mostRecent = result; + } + } + } + return mostRecent; } @Override @@ -570,7 +748,48 @@ public class WifiConfiguration implements Parcelable { return KeyMgmt.NONE; } - /** Implement the Parcelable interface {@hide} */ + /* @hide + * Cache the config key, this seems useful as a speed up since a lot of + * lookups in the config store are done and based on this key. + */ + String mCachedConfigKey; + + /** @hide + * return the string used to calculate the hash in WifiConfigStore + * and uniquely identify this WifiConfiguration + */ + public String configKey(boolean allowCached) { + String key; + if (allowCached && mCachedConfigKey != null) { + key = mCachedConfigKey; + } else { + key = this.SSID; + if (key == null) + key = ""; + if (this.wepKeys[0] != null) { + key = key + "-WEP"; + } + if (this.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { + key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK]; + } + if (this.allowedKeyManagement.get(KeyMgmt.WPA_EAP) || + this.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { + key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP]; + } + mCachedConfigKey = key; + } + return key; + } + + /** @hide + * get configKey, force calculating the config string + */ + public String configKey() { + return configKey(false); + } + + + /** Implement the Parcelable interface {@hide} */ public int describeContents() { return 0; } @@ -603,8 +822,32 @@ public class WifiConfiguration implements Parcelable { ipAssignment = source.ipAssignment; proxySettings = source.proxySettings; + + defaultGwMacAddress = source.defaultGwMacAddress; + linkProperties = new LinkProperties(source.linkProperties); - } + if ((source.scanResultCache != null) && (source.scanResultCache.size() > 0)) { + scanResultCache = new HashMap<String, ScanResult>(); + scanResultCache.putAll(source.scanResultCache); + } + + if ((source.connectChoices != null) && (source.connectChoices.size() > 0)) { + connectChoices = new HashMap<String, Integer>(); + connectChoices.putAll(source.connectChoices); + } + + if ((source.linkedConfigurations != null) + && (source.linkedConfigurations.size() > 0)) { + linkedConfigurations = new HashMap<String, Integer>(); + linkedConfigurations.putAll(source.linkedConfigurations); + } + mCachedConfigKey = null; //force null configKey + autoJoinStatus = source.autoJoinStatus; + + if (source.visibility != null) { + visibility = new Visibility(source.visibility); + } + } } /** Implement the Parcelable interface {@hide} */ @@ -633,6 +876,10 @@ public class WifiConfiguration implements Parcelable { dest.writeString(ipAssignment.name()); dest.writeString(proxySettings.name()); dest.writeParcelable(linkProperties, flags); + + dest.writeString(dhcpServer); + dest.writeString(defaultGwMacAddress); + dest.writeInt(autoJoinStatus); } /** Implement the Parcelable interface {@hide} */ @@ -664,6 +911,10 @@ public class WifiConfiguration implements Parcelable { config.proxySettings = ProxySettings.valueOf(in.readString()); config.linkProperties = in.readParcelable(null); + config.dhcpServer = in.readString(); + config.defaultGwMacAddress = in.readString(); + config.autoJoinStatus = in.readInt(); + return config; } diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java new file mode 100644 index 0000000..e02e14c --- /dev/null +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -0,0 +1,584 @@ +/* + * Copyright (C) 2008 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.net.wifi; + +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.util.AsyncChannel; +import com.android.internal.util.Protocol; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; + + +/** + * This class provides a way to scan the Wifi universe around the device + * Get an instance of this class by calling + * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context + * .WIFI_SCANNING_SERVICE)}. + * @hide + */ +public class WifiScanner { + + public static final int WIFI_BAND_UNSPECIFIED = 0; /* not specified */ + public static final int WIFI_BAND_24_GHZ = 1; /* 2.4 GHz band */ + public static final int WIFI_BAND_5_GHZ = 2; /* 5 GHz band without DFS channels */ + public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; /* 5 GHz band with DFS channels */ + public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; /* 5 GHz band with DFS channels */ + public static final int WIFI_BAND_BOTH = 3; /* both bands without DFS channels */ + public static final int WIFI_BAND_BOTH_WITH_DFS = 7; /* both bands with DFS channels */ + + public static final int MIN_SCAN_PERIOD_MS = 300; /* minimum supported period */ + public static final int MAX_SCAN_PERIOD_MS = 1024000; /* maximum supported period */ + + public static final int REASON_SUCCEEDED = 0; + public static final int REASON_UNSPECIFIED = -1; + public static final int REASON_INVALID_LISTENER = -2; + public static final int REASON_INVALID_REQUEST = -3; + public static final int REASON_CONFLICTING_REQUEST = -4; + + public static interface ActionListener { + public void onSuccess(Object result); + public void onFailure(int reason, Object exception); + } + + /** + * gives you all the possible channels; channel is specified as an + * integer with frequency in MHz i.e. channel 1 is 2412 + */ + public List<Integer> getAvailableChannels(int band) { + return null; + } + + /** + * provides channel specification to the APIs + */ + public static class ChannelSpec { + public int frequency; + public boolean passive; /* ignored on DFS channels */ + public int dwellTimeMS; /* not supported for now */ + + public ChannelSpec(int frequency) { + this.frequency = frequency; + passive = false; + dwellTimeMS = 0; + } + } + + public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0; + public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; + public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; + + /** + * scan configuration parameters + */ + public static class ScanSettings implements Parcelable { + + public int band; /* ignore channels if specified */ + public ChannelSpec[] channels; /* list of channels to scan */ + public int periodInMs; /* period of scan */ + public int reportEvents; /* a valid REPORT_EVENT value */ + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(band); + dest.writeInt(periodInMs); + dest.writeInt(channels.length); + + for (int i = 0; i < channels.length; i++) { + dest.writeInt(channels[i].frequency); + dest.writeInt(channels[i].dwellTimeMS); + dest.writeInt(channels[i].passive ? 1 : 0); + } + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<ScanSettings> CREATOR = + new Creator<ScanSettings>() { + public ScanSettings createFromParcel(Parcel in) { + + ScanSettings settings = new ScanSettings(); + settings.band = in.readInt(); + settings.periodInMs = in.readInt(); + int num_channels = in.readInt(); + settings.channels = new ChannelSpec[num_channels]; + for (int i = 0; i < num_channels; i++) { + int frequency = in.readInt(); + + ChannelSpec spec = new ChannelSpec(frequency); + spec.dwellTimeMS = in.readInt(); + spec.passive = in.readInt() == 1; + settings.channels[i] = spec; + } + + return settings; + } + + public ScanSettings[] newArray(int size) { + return new ScanSettings[size]; + } + }; + + } + + public static class InformationElement { + public int id; + public byte[] bytes; + } + + public static class FullScanResult { + public ScanResult result; + public InformationElement informationElements[]; + } + + /** @hide */ + public static class ParcelableScanResults implements Parcelable { + public ScanResult mResults[]; + + public ParcelableScanResults(ScanResult[] results) { + mResults = results; + } + + public ScanResult[] getResults() { + return mResults; + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mResults.length); + for (int i = 0; i < mResults.length; i++) { + ScanResult result = mResults[i]; + result.writeToParcel(dest, flags); + } + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<ParcelableScanResults> CREATOR = + new Creator<ParcelableScanResults>() { + public ParcelableScanResults createFromParcel(Parcel in) { + int n = in.readInt(); + ScanResult results[] = new ScanResult[n]; + for (int i = 0; i < n; i++) { + results[i] = ScanResult.CREATOR.createFromParcel(in); + } + return new ParcelableScanResults(results); + } + + public ParcelableScanResults[] newArray(int size) { + return new ParcelableScanResults[size]; + } + }; + } + + /** + * Framework is co-ordinating scans across multiple apps; so it may not give exactly the + * same period requested. The period granted is stated on the onSuccess() event; and + * onPeriodChanged() will be called if/when it is changed because of multiple conflicting + * requests. This is similar to the way timers are handled. + */ + public interface ScanListener extends ActionListener { + public void onPeriodChanged(int periodInMs); + public void onResults(ScanResult[] results); + public void onFullResult(FullScanResult fullScanResult); + } + + public void scan(ScanSettings settings, ScanListener listener) { + validateChannel(); + sAsyncChannel.sendMessage(CMD_SCAN, 0, putListener(listener), settings); + } + public void startBackgroundScan(ScanSettings settings, ScanListener listener) { + validateChannel(); + sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, putListener(listener), settings); + } + public void stopBackgroundScan(boolean flush, ScanListener listener) { + validateChannel(); + sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, removeListener(listener)); + } + public void retrieveScanResults(boolean flush, ScanListener listener) { + validateChannel(); + sAsyncChannel.sendMessage(CMD_GET_SCAN_RESULTS, 0, getListenerKey(listener)); + } + + public static class HotspotInfo { + public String bssid; + public int low; /* minimum RSSI */ + public int high; /* maximum RSSI */ + } + + public static class WifiChangeSettings { + public int rssiSampleSize; /* sample size for RSSI averaging */ + public int lostApSampleSize; /* samples to confirm AP's loss */ + public int unchangedSampleSize; /* samples to confirm no change */ + public int minApsBreachingThreshold; /* change threshold to trigger event */ + public HotspotInfo[] hotspotInfos; + } + + /* overrides the significant wifi change state machine configuration */ + public void configureSignificantWifiChange( + int rssiSampleSize, /* sample size for RSSI averaging */ + int lostApSampleSize, /* samples to confirm AP's loss */ + int unchangedSampleSize, /* samples to confirm no change */ + int minApsBreachingThreshold, /* change threshold to trigger event */ + HotspotInfo[] hotspotInfos /* signal thresholds to crosss */ + ) + { + validateChannel(); + WifiChangeSettings settings = new WifiChangeSettings(); + settings.rssiSampleSize = rssiSampleSize; + settings.lostApSampleSize = lostApSampleSize; + settings.unchangedSampleSize = unchangedSampleSize; + settings.minApsBreachingThreshold = minApsBreachingThreshold; + settings.hotspotInfos = hotspotInfos; + + sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings); + } + + public interface SignificantWifiChangeListener extends ActionListener { + public void onChanging(ScanResult[] results); /* changes are found */ + public void onQuiescence(ScanResult[] results); /* changes settled down */ + } + + public void trackSignificantWifiChange(SignificantWifiChangeListener listener) { + validateChannel(); + sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, putListener(listener)); + } + public void untrackSignificantWifiChange(SignificantWifiChangeListener listener) { + validateChannel(); + sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, removeListener(listener)); + } + + public void configureSignificantWifiChange(WifiChangeSettings settings) { + validateChannel(); + sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings); + } + + public static interface HotlistListener extends ActionListener { + public void onFound(ScanResult[] results); + } + + /** @hide */ + public static class HotlistSettings implements Parcelable { + public HotspotInfo[] hotspotInfos; + public int apLostThreshold; + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(apLostThreshold); + dest.writeInt(hotspotInfos.length); + for (int i = 0; i < hotspotInfos.length; i++) { + HotspotInfo info = hotspotInfos[i]; + dest.writeString(info.bssid); + dest.writeInt(info.low); + dest.writeInt(info.high); + } + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<HotlistSettings> CREATOR = + new Creator<HotlistSettings>() { + public HotlistSettings createFromParcel(Parcel in) { + HotlistSettings settings = new HotlistSettings(); + settings.apLostThreshold = in.readInt(); + int n = in.readInt(); + settings.hotspotInfos = new HotspotInfo[n]; + for (int i = 0; i < n; i++) { + HotspotInfo info = new HotspotInfo(); + info.bssid = in.readString(); + info.low = in.readInt(); + info.high = in.readInt(); + settings.hotspotInfos[i] = info; + } + return settings; + } + + public HotlistSettings[] newArray(int size) { + return new HotlistSettings[size]; + } + }; + } + + public void setHotlist(HotspotInfo[] hotspots, + int apLostThreshold, HotlistListener listener) { + validateChannel(); + HotlistSettings settings = new HotlistSettings(); + settings.hotspotInfos = hotspots; + sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, putListener(listener), settings); + } + + public void resetHotlist(HotlistListener listener) { + validateChannel(); + sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener)); + } + + + /* private members and methods */ + + private static final String TAG = "WifiScanner"; + private static final boolean DBG = true; + + /* commands for Wifi Service */ + private static final int BASE = Protocol.BASE_WIFI_SCANNER; + + /** @hide */ + public static final int CMD_SCAN = BASE + 0; + /** @hide */ + public static final int CMD_START_BACKGROUND_SCAN = BASE + 2; + /** @hide */ + public static final int CMD_STOP_BACKGROUND_SCAN = BASE + 3; + /** @hide */ + public static final int CMD_GET_SCAN_RESULTS = BASE + 4; + /** @hide */ + public static final int CMD_SCAN_RESULT = BASE + 5; + /** @hide */ + public static final int CMD_SET_HOTLIST = BASE + 6; + /** @hide */ + public static final int CMD_RESET_HOTLIST = BASE + 7; + /** @hide */ + public static final int CMD_AP_FOUND = BASE + 9; + /** @hide */ + public static final int CMD_AP_LOST = BASE + 10; + /** @hide */ + public static final int CMD_START_TRACKING_CHANGE = BASE + 11; + /** @hide */ + public static final int CMD_STOP_TRACKING_CHANGE = BASE + 12; + /** @hide */ + public static final int CMD_CONFIGURE_WIFI_CHANGE = BASE + 13; + /** @hide */ + public static final int CMD_WIFI_CHANGE_DETECTED = BASE + 15; + /** @hide */ + public static final int CMD_WIFI_CHANGES_STABILIZED = BASE + 16; + /** @hide */ + public static final int CMD_OP_SUCCEEDED = BASE + 17; + /** @hide */ + public static final int CMD_OP_FAILED = BASE + 18; + /** @hide */ + public static final int CMD_PERIOD_CHANGED = BASE + 19; + /** @hide */ + public static final int CMD_FULL_SCAN_RESULT = BASE + 20; + + private Context mContext; + private IWifiScanner mService; + + private static final int INVALID_KEY = 0; + private static int sListenerKey = 1; + + private static final SparseArray sListenerMap = new SparseArray(); + private static final Object sListenerMapLock = new Object(); + + private static AsyncChannel sAsyncChannel; + private static CountDownLatch sConnected; + + private static final Object sThreadRefLock = new Object(); + private static int sThreadRefCount; + private static HandlerThread sHandlerThread; + + /** + * Create a new WifiScanner instance. + * Applications will almost always want to use + * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve + * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}. + * @param context the application context + * @param service the Binder interface + * @hide + */ + public WifiScanner(Context context, IWifiScanner service) { + mContext = context; + mService = service; + init(); + } + + private void init() { + synchronized (sThreadRefLock) { + if (++sThreadRefCount == 1) { + Messenger messenger = null; + try { + messenger = mService.getMessenger(); + } catch (RemoteException e) { + /* do nothing */ + } catch (SecurityException e) { + /* do nothing */ + } + + if (messenger == null) { + sAsyncChannel = null; + return; + } + + sHandlerThread = new HandlerThread("WifiScanner"); + sAsyncChannel = new AsyncChannel(); + sConnected = new CountDownLatch(1); + + sHandlerThread.start(); + Handler handler = new ServiceHandler(sHandlerThread.getLooper()); + sAsyncChannel.connect(mContext, handler, messenger); + try { + sConnected.await(); + } catch (InterruptedException e) { + Log.e(TAG, "interrupted wait at init"); + } + } + } + } + + private void validateChannel() { + if (sAsyncChannel == null) throw new IllegalStateException( + "No permission to access and change wifi or a bad initialization"); + } + + private static int putListener(Object listener) { + if (listener == null) return INVALID_KEY; + int key; + synchronized (sListenerMapLock) { + do { + key = sListenerKey++; + } while (key == INVALID_KEY); + sListenerMap.put(key, listener); + } + return key; + } + + private static Object getListener(int key) { + if (key == INVALID_KEY) return null; + synchronized (sListenerMapLock) { + Object listener = sListenerMap.get(key); + return listener; + } + } + + private static int getListenerKey(Object listener) { + if (listener == null) return INVALID_KEY; + synchronized (sListenerMapLock) { + int index = sListenerMap.indexOfValue(listener); + if (index == -1) { + return INVALID_KEY; + } else { + return sListenerMap.keyAt(index); + } + } + } + + private static Object removeListener(int key) { + if (key == INVALID_KEY) return null; + synchronized (sListenerMapLock) { + Object listener = sListenerMap.get(key); + sListenerMap.remove(key); + return listener; + } + } + + private static int removeListener(Object listener) { + int key = getListenerKey(listener); + if (key == INVALID_KEY) return key; + synchronized (sListenerMapLock) { + sListenerMap.remove(key); + return key; + } + } + + private static class ServiceHandler extends Handler { + ServiceHandler(Looper looper) { + super(looper); + } + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: + if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { + sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + } else { + Log.e(TAG, "Failed to set up channel connection"); + // This will cause all further async API calls on the WifiManager + // to fail and throw an exception + sAsyncChannel = null; + } + sConnected.countDown(); + return; + case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: + return; + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: + Log.e(TAG, "Channel connection lost"); + // This will cause all further async API calls on the WifiManager + // to fail and throw an exception + sAsyncChannel = null; + getLooper().quit(); + return; + } + + Object listener = getListener(msg.arg2); + if (DBG) Log.d(TAG, "listener key = " + msg.arg2); + + switch (msg.what) { + /* ActionListeners grouped together */ + case CMD_OP_SUCCEEDED : + ((ActionListener) listener).onSuccess(msg.obj); + break; + case CMD_OP_FAILED : + ((ActionListener) listener).onFailure(msg.arg1, msg.obj); + break; + case CMD_SCAN_RESULT : + ((ScanListener) listener).onResults( + ((ParcelableScanResults) msg.obj).getResults()); + return; + case CMD_FULL_SCAN_RESULT : + FullScanResult result = (FullScanResult) msg.obj; + ((ScanListener) listener).onFullResult(result); + return; + case CMD_AP_FOUND: + ((HotlistListener) listener).onFound( + ((ParcelableScanResults) msg.obj).getResults()); + return; + case CMD_WIFI_CHANGE_DETECTED: + ((SignificantWifiChangeListener) listener).onChanging( + ((ParcelableScanResults) msg.obj).getResults()); + return; + case CMD_WIFI_CHANGES_STABILIZED: + ((SignificantWifiChangeListener) listener).onQuiescence( + ((ParcelableScanResults) msg.obj).getResults()); + return; + default: + if (DBG) Log.d(TAG, "Ignoring message " + msg.what); + return; + } + } + } +} |
